diff --git a/packages/CLI11/.appveyor.yml b/packages/CLI11/.appveyor.yml
index 91a95a011ef5e8e38e5433e3464abb9720a7987f..2945118701113a77ea5735037baa2082899957c6 100644
--- a/packages/CLI11/.appveyor.yml
+++ b/packages/CLI11/.appveyor.yml
@@ -3,6 +3,7 @@ branches:
     - master
 
 install:
+  - git submodule update --init --recursive
   - set PATH=C:\Python36;%PATH%
   - cmake --version
   - pip install conan
diff --git a/packages/CLI11/.ci/build_docs.sh b/packages/CLI11/.ci/build_docs.sh
index b8fab375429ae4ca692c6497866f8fb9cd8ea6b4..d1f68335d2e14146935560b8d4cbf7deedbbf129 100755
--- a/packages/CLI11/.ci/build_docs.sh
+++ b/packages/CLI11/.ci/build_docs.sh
@@ -3,7 +3,7 @@
 # Title         : generateDocumentationAndDeploy.sh
 # Date created  : 2016/02/22
 # Notes         :
-__AUTHOR__="Jeroen de Bruijn"
+# Author        : Jeroen de Bruijn
 # Preconditions:
 # - Packages doxygen doxygen-doc doxygen-latex doxygen-gui graphviz
 #   must be installed.
@@ -29,7 +29,7 @@ __AUTHOR__="Jeroen de Bruijn"
 # the gh-pages branch of a repository specified by GH_REPO_REF.
 # Before this script is used there should already be a gh-pages branch in the
 # repository.
-# 
+#
 ################################################################################
 
 ################################################################################
@@ -38,8 +38,8 @@ echo 'Setting up the script...'
 # Exit with nonzero exit code if anything fails
 set -e
 
-GH_REPO_ORG=`echo $TRAVIS_REPO_SLUG | cut -d "/" -f 1`
-GH_REPO_NAME=`echo $TRAVIS_REPO_SLUG | cut -d "/" -f 2`
+GH_REPO_ORG=$(echo $TRAVIS_REPO_SLUG | cut -d "/" -f 1)
+GH_REPO_NAME=$(echo $TRAVIS_REPO_SLUG | cut -d "/" -f 2)
 GH_REPO_REF="github.com/$GH_REPO_ORG/$GH_REPO_NAME.git"
 
 # Create a clean working directory for this script.
diff --git a/packages/CLI11/.ci/build_doxygen.sh b/packages/CLI11/.ci/build_doxygen.sh
index 6772ecf97e98f4fbc4f84a51da18e8cc1d6cf724..97dd4e1e2867571350ff43023e7188dd383111c7 100644
--- a/packages/CLI11/.ci/build_doxygen.sh
+++ b/packages/CLI11/.ci/build_doxygen.sh
@@ -1,3 +1,6 @@
+#!/bin/env sh
+# (Source me)
+
 set -evx
 
 DOXYGEN_URL="ftp://ftp.stack.nl/pub/users/dimitri/doxygen-1.8.13.src.tar.gz"
diff --git a/packages/CLI11/.ci/build_lcov.sh b/packages/CLI11/.ci/build_lcov.sh
index 8671d7881308cc3ac76908ff69764889c54430d8..7232e9987e0338ae2ba9ee7cf419e8df39cc1312 100644
--- a/packages/CLI11/.ci/build_lcov.sh
+++ b/packages/CLI11/.ci/build_lcov.sh
@@ -1,3 +1,5 @@
+#!/bin/env sh
+# (Source me)
 set -evx
 
 LCOV_URL="http://ftp.de.debian.org/debian/pool/main/l/lcov/lcov_1.13.orig.tar.gz"
diff --git a/packages/CLI11/.codecov.yml b/packages/CLI11/.codecov.yml
index a53035820b6c1fa93a6f9c3cf9599a41db29ee1b..4181c549820b70653821288e19344606eaba55f1 100644
--- a/packages/CLI11/.codecov.yml
+++ b/packages/CLI11/.codecov.yml
@@ -1,3 +1,4 @@
 
 ignore:
   - "tests"
+  - "examples"
diff --git a/packages/CLI11/.github/CONTRIBUTING.md b/packages/CLI11/.github/CONTRIBUTING.md
index c93c400b42c70fbda73022faad274e72e415b1cb..f09e1f2870afb277009a8116c92c767b7cb2580d 100644
--- a/packages/CLI11/.github/CONTRIBUTING.md
+++ b/packages/CLI11/.github/CONTRIBUTING.md
@@ -29,11 +29,10 @@ And, if you want to always use it, feel free to install the git hook provided in
 
 ## For developers releasing to Conan.io
 
-The steps to make a Conan.io release are:
+This is now done by the CI system on tagged releases. Previously, the steps to make a Conan.io release were:
 
 ```bash
-conan delete '*' # optional, I like to be clean
-
+conan remove '*' # optional, I like to be clean
 conan create . cliutils/stable
 conan upload "*" -r cli11 --all
 ```
diff --git a/packages/CLI11/.gitmodules b/packages/CLI11/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..2b5117d2cbaf0a48723d833530996594d19a37a3
--- /dev/null
+++ b/packages/CLI11/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "extern/googletest"]
+	path = extern/googletest
+	url = ../../google/googletest.git
+[submodule "extern/sanitizers"]
+	path = extern/sanitizers
+	url = ../../arsenm/sanitizers-cmake
diff --git a/packages/CLI11/.gitrepo b/packages/CLI11/.gitrepo
index 19886d90e79da7583fb45f6afb638364f3ed406c..0bd709b9b6e8c72c9f26be4e626c526327955c1e 100644
--- a/packages/CLI11/.gitrepo
+++ b/packages/CLI11/.gitrepo
@@ -6,6 +6,6 @@
 [subrepo]
 	remote = git@github.com:CLIUtils/CLI11.git
 	branch = master
-	commit = df5b157d97530ed605532e7ed0799fdf6659bbc3
-	parent = 3c3894389721d339e70c9996ab528e8795d3f723
+	commit = 6f41cca9189129fd6df8c82bff532693dcfe52c3
+	parent = e7630b332e1d23511b2de021692cf478675a662b
 	cmdver = 0.3.1
diff --git a/packages/CLI11/.travis.yml b/packages/CLI11/.travis.yml
index ac8ba76192e513b29dc6df5e0e4fe7567f4d86a0..80bdfbfbab0240161469308a5324679ccb2474bd 100644
--- a/packages/CLI11/.travis.yml
+++ b/packages/CLI11/.travis.yml
@@ -96,7 +96,7 @@ matrix:
     script:
     - .ci/make_and_test.sh 11
     after_success:
-    - conan create . CLIUtils/stable
+    - conan create . cliutils/stable
     - |
       if [ "${TRAVIS_TAG}" ]
       then
@@ -111,13 +111,9 @@ matrix:
     install:
     - brew update
     - echo 'brew "python"' > Brewfile
-    - echo 'brew "conan"' >> Brewfile
     - echo 'brew "ccache"' >> Brewfile
     - brew bundle
     - python -m ensurepip --user
-    - conan user
-    after_success:
-    - conan create . CLIUtils/CLI11
 
 install: skip
 
diff --git a/packages/CLI11/CHANGELOG.md b/packages/CLI11/CHANGELOG.md
index 0262b0e0cdde94a1a293b0f61490bfe4552c1dae..d9b05d299e331bbacc5561452d50331d7140f11a 100644
--- a/packages/CLI11/CHANGELOG.md
+++ b/packages/CLI11/CHANGELOG.md
@@ -1,8 +1,94 @@
-## In progress
+## Version 1.6: Formatting help
 
-* Make unlimited positionals vs. unlimited options more intuitive [#102]
+Added a new formatting system [#109]. You can now set the formatter on Apps. This has also simplified the internals of Apps and Options a bit by separating most formatting code.
+
+* Added `CLI::Formatter` and `formatter` slot for apps, inherited.
+* `FormatterBase` is the minimum required
+* `FormatterLambda` provides for the easy addition of an arbitrary function
+* Added `help_all` support (not added by default)
+
+Changes to the help system (most normal users will not notice this):
+
+* Renamed `single_name` to `get_name(false, false)` (the default)
+* The old `get_name()` is now `get_name(false, true)`
+* The old `get_pname()` is now `get_name(true, false)`
+* Removed `help_*` functions
+* Protected function `_has_help_positional` removed
+* `format_help` can now be chained
+
+
+New for Config file reading and writing [#121]:
+
+* Overridable, bidirectional Config
+* ConfigINI provided and used by default
+* Renamed ini to config in many places
+* Has `config_formatter()` and `get_config_formatter()`
+* Dropped prefix argument from `config_to_str`
+* Added `ConfigItem`
+
+
+Validators are now much more powerful [#118], all built in validators upgraded to the new form:
+
+* A subclass of `CLI::Validator` is now also accepted.
+* They now can set the type name to things like `PATH` and `INT in [1-4]`.
+* Validators can be combined with `&` and `|`.
+* Old form simple validators are still accepted.
+
+Other changes:
+
+* Added `->each()` to make adding custom callbacks easier [#126]
+* Added filter argument to `get_subcommands`, `get_options`; use empty filter `{}` to avoid filtering
+* Added `get_groups()` to get groups
+* Added getters for the missing parts of options (help no longer uses any private parts)
+* Better support for manual options with `get_option`, `set_results`, and `empty` [#119]
+* `lname` and `sname` have getters, added `const get_parent` [#120]
+* Using `add_set` will now capture L-values for sets, allowing further modification [#113]
+* Internally, `type_name` is now a lambda function; for sets, this reads the set live [#116] 
+* Dropped duplicate way to run `get_type_name` (`get_typeval`)
+* Testing (only) now uses submodules. [#111]
+* Removed `requires` in favor of `needs` (deprecated in last version) [#112]
+* Better CMake policy handling [#110]
+* Includes are properly sorted [#120]
+* Help flags now use new `short_circuit` property to simplify parsing [#121]
+
+[#109]: https://github.com/CLIUtils/CLI11/pull/109
+[#110]: https://github.com/CLIUtils/CLI11/pull/110
+[#111]: https://github.com/CLIUtils/CLI11/pull/111
+[#112]: https://github.com/CLIUtils/CLI11/pull/112
+[#113]: https://github.com/CLIUtils/CLI11/issues/113
+[#116]: https://github.com/CLIUtils/CLI11/pull/116
+[#118]: https://github.com/CLIUtils/CLI11/pull/118
+[#119]: https://github.com/CLIUtils/CLI11/pull/119
+[#120]: https://github.com/CLIUtils/CLI11/pull/120
+[#121]: https://github.com/CLIUtils/CLI11/pull/121
+[#126]: https://github.com/CLIUtils/CLI11/pull/126
+
+### Version 1.5.4: Optionals
+This version fixes the optional search in the single file version; some macros were not yet defined when it did the search. You can define the `CLI11_*_OPTIONAL` macros to 0 if needed to eliminate the search.
+
+### Version 1.5.3: Compiler compatibility
+This version fixes older AppleClang compilers by removing the optimization for casting. The minimum version of Boost Optional supported has been clarified to be 1.58. CUDA 7.0 NVCC is now supported.
+
+### Version 1.5.2: LICENSE in single header mode
+
+This is a quick patch release that makes LICENSE part of the single header file, making it easier to include. Minor cleanup from codacy. No significant code changes from 1.5.1.
+
+### Version 1.5.1: Access
+
+This patch release adds better access to the App progromatically, to assist with writing custom converters to other formats. It also improves the help output, and uses a new feature in CLI11 1.5 to fix an old "quirk" in the way unlimited options and positionals interact.
+
+* Make mixing unlimited positionals and options more intuitive [#102]
+* Add missing getters `get_options` and `get_description` to App [#105]
+* The app name now can be set, and will override the auto name if present [#105]
+* Add `(REQUIRED)` for required options [#104]
+* Print simple name for Needs/Excludes [#104]
+* Use Needs instead of Requires in help print [#104]
+* Groups now are listed in the original definition order [#106]
 
 [#102]: https://github.com/CLIUtils/CLI11/issues/102
+[#104]: https://github.com/CLIUtils/CLI11/pull/104
+[#105]: https://github.com/CLIUtils/CLI11/pull/105
+[#106]: https://github.com/CLIUtils/CLI11/pull/106
 
 
 ## Version 1.5: Optionals
diff --git a/packages/CLI11/CMakeLists.txt b/packages/CLI11/CMakeLists.txt
index 040ae0e0033878a5e0d0b4575f5d74f9faf3b14d..1fe8248365dcab530e9d2fc6ac56a6a837e2599c 100644
--- a/packages/CLI11/CMakeLists.txt
+++ b/packages/CLI11/CMakeLists.txt
@@ -1,4 +1,8 @@
-cmake_minimum_required(VERSION 3.4 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.4...3.11)
+
+if(${CMAKE_VERSION} VERSION_LESS 3.12)
+    cmake_policy(VERSION ${CMAKE_VERSION})
+endif()
 
 set(VERSION_REGEX "#define CLI11_VERSION[ \t]+\"(.+)\"")
 
@@ -57,11 +61,6 @@ include(CMakeDependentOption)
 # Allow IDE's to group targets into folders
 set_property(GLOBAL PROPERTY USE_FOLDERS ON)
 
-if(CMAKE_BUILD_TYPE STREQUAL Coverage)
-    include(CodeCoverage)
-    setup_target_for_coverage(CLI11_coverage ctest coverage)
-endif()
-
 file(GLOB CLI11_headers "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/*")
 # To see in IDE, must be listed for target
 
@@ -136,7 +135,7 @@ if(CLI11_SINGLE_FILE)
     target_include_directories(CLI11_SINGLE INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include/")
 endif()
 
-option(CLI11_SINGLE_FILE_TESTS "Duplicate all the tests for a single file build" OFF)
+cmake_dependent_option(CLI11_SINGLE_FILE_TESTS "Duplicate all the tests for a single file build" OFF "CLI11_SINGLE_FILE" OFF)
 
 cmake_dependent_option(CLI11_TESTING "Build the tests and add them" ON "CUR_PROJ" OFF)
 if(CLI11_TESTING)
diff --git a/packages/CLI11/LICENSE b/packages/CLI11/LICENSE
index ac56c9881192b8417cb9fdcf3af5344a6cd44f8f..d3d8f0122469ff0545f94dee5ac22854975e5131 100644
--- a/packages/CLI11/LICENSE
+++ b/packages/CLI11/LICENSE
@@ -1,11 +1,25 @@
-CLI11 1.0 Copyright (c) 2017 University of Cincinnati, developed by Henry Schreiner under NSF AWARD 1414736.
-All rights reserved.
+CLI11 1.5 Copyright (c) 2017-2018 University of Cincinnati, developed by Henry
+Schreiner under NSF AWARD 1414736. All rights reserved.
 
-Redistribution and use in source and binary forms of CLI11, with or without modification, are permitted provided that the following conditions are met:
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+Redistribution and use in source and binary forms of CLI11, with or without
+modification, are permitted provided that the following conditions are met:
 
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer. 
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the names of its contributors
+   may be used to endorse or promote products derived from this software without
+   specific prior written permission.
 
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/CLI11/README.md b/packages/CLI11/README.md
index 4fd12b517afa27e95fcaf8b6a62c5efe81237ded..10c50049df5145d25f11b012e160b1a37b2d667a 100644
--- a/packages/CLI11/README.md
+++ b/packages/CLI11/README.md
@@ -1,16 +1,17 @@
 [![Build Status Linux and macOS][travis-badge]][Travis]
 [![Build Status Windows][appveyor-badge]][AppVeyor]
 [![Code Coverage][codecov-badge]][CodeCov]
+[![Codacy Badge][codacy-badge]][codacy-link]
 [![Join the chat at https://gitter.im/CLI11gitter/Lobby][gitter-badge]][gitter]
 [![License: BSD][license-badge]](./LICENSE)
 [![Latest release][releases-badge]][Github Releases]
 [![DOI][DOI-badge]][DOI-link]
 [![Conan.io][conan-badge]][conan-link]
+[![Try CLI11 1.5.2 online][wandbox-badge]][wandbox-link]
 
 [Documentation][GitBook] •
 [API Reference][api-docs] •
-[What's new](./CHANGELOG.md) •
-[Try CLI11 1.5 online][wandbox-link]
+[What's new](./CHANGELOG.md)
 
 # CLI11: Command line parser for C++11
 
@@ -24,9 +25,9 @@ You can be notified when new releases are made by subscribing to https://github.
 
 An acceptable CLI parser library should be all of the following:
 
-* Easy to include (i.e., header only, one file if possible, **no external requirements**): While many programs depend on Boost, that should not be a requirement if all you want is CLI parsing.
-* Short Syntax: This is one of the main points of a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
-* C++11 or better: Should work with GCC 4.7+ (such as GCC 4.8 on CentOS 7) or above, or Clang 3.5+, or MSVC 2015+. (Note: for CLI11, Clang 3.4 only fails because of tests, GoogleMock does not support it.)
+* Easy to include (i.e., header only, one file if possible, **no external requirements**).
+* Short, simple syntax: This is one of the main reasons to use a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
+* C++11 or better: Should work with GCC 4.7+ (such as GCC 4.8 on CentOS 7), Clang 3.5+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+.
 * Work on Linux, macOS, and Windows.
 * Well tested using [Travis] (Linux and macOS) and [AppVeyor] (Windows). "Well" is defined as having good coverage measured by [CodeCov].
 * Clear help printing.
@@ -88,7 +89,7 @@ To use, there are two methods:
 
 1. Copy `CLI11.hpp` from the [most recent release][Github Releases] into your include directory, and you are set. This is combined from the source files  for every release. This includes the entire command parser library, but does not include separate utilities (like `Timer`, `AutoTimer`). The utilities are completely self contained and can be copied separately.
 2. Use `CLI/*.hpp` files. You could check out the repository as a submodule, for example. You can use the `CLI11::CLI11` interface target when linking from `add_subdirectory`.
-   You can also configure and optionally install the project, and then use `find_package(CLI11 CONFIG)` to get the `CLI11::CLI11` target. You can also use [Conan.io][conan-link].
+   You can also configure and optionally install the project, and then use `find_package(CLI11 CONFIG)` to get the `CLI11::CLI11` target. You can also use [Conan.io][conan-link] or [Hunter].
    (These are just conveniences to allow you to use your favorite method of managing packages; it's just header only so including the correct path and
    using C++11 is all you really need.)
 
@@ -162,11 +163,11 @@ app.add_set_ignore_case(... // String only
 App* subcom = app.add_subcommand(name, discription);
 ```
 
-An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`. The set options allow your users to pick from a set of predefined options.
+An option name must start with a alphabetic character or underscore. For long options, anything but an equals sign or a comma is valid after that. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option` or `add_set`. The set options allow your users to pick from a set of predefined options; you can add an existing set if you need to modify the set later, or you can use an initializer list.
 
 On a C++14 compiler, you can pass a callback function directly to `.add_flag`, while in C++11 mode you'll need to use `.add_flag_function` if you want a callback function. The function will be given the number of times the flag was passed. You can throw a relevant `CLI::ParseError` to signal a failure.
 
-On a compiler that supports C++17's `__has_include`, you can also use `std::optional`, `std::experimental::optional`, and `boost::optional` directly in an `add_option` call. If you don't have `__has_include`, you can define `CLI11_BOOST_OPTIONAL` before including CLI11 to manually add support for `boost::optional`. See [CLI11 Internals] for information on how this was done and how you can add your own converters.
+On a compiler that supports C++17's `__has_include`, you can also use `std::optional`, `std::experimental::optional`, and `boost::optional` directly in an `add_option` call. If you don't have `__has_include`, you can define `CLI11_BOOST_OPTIONAL 1` before including CLI11 to manually add support (or 0 to remove) for `boost::optional`. See [CLI11 Internals] for information on how this was done and how you can add your own converters.
 
 ### Example
 
@@ -179,6 +180,7 @@ The add commands return a pointer to an internally stored `Option`. If you set t
 
 * `->required()`: The program will quit if this option is not present. This is `mandatory` in Plumbum, but required options seems to be a more standard term. For compatibility, `->mandatory()` also works.
 * `->expected(N)`: Take `N` values instead of as many as possible, only for vector args. If negative, require at least `-N`; end with `--` or another recognized option.
+* `->set_custom_option(typename, N)`: Set the name and (optional) intrinsic size of an option. The parser will require multiples of this number if negative.
 * `->needs(opt)`: This option requires another option to also be present, opt is an `Option` pointer.
 * `->excludes(opt)`: This option cannot be given with `opt` present, opt is an `Option` pointer.
 * `->envname(name)`: Gets the value from the environment if present and not passed on the command line.
@@ -191,9 +193,10 @@ The add commands return a pointer to an internally stored `Option`. If you set t
 * `->check(CLI::NonexistentPath)`: Requires that the path does not exist.
 * `->check(CLI::Range(min,max))`: Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.
 * `->transform(std::string(std::string))`: Converts the input string into the output string, in-place in the parsed options.
-* `->configurable(false)`: Disable this option from being in an ini configuration file.
+* `->each(void(std::string)>`: Run this function on each value received, as it is received.
+* `->configurable(false)`: Disable this option from being in a configuration file.
 
-These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results.
+These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. Check takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results. Validate can also be a subclass of `CLI::Validator`, in which case it can also set the type name and can be combined with `&` and `|` (all built-in validators are this sort).
 
 
 On the command line, options can be given as:
@@ -241,18 +244,26 @@ There are several options that are supported on the main app and subcommands. Th
 * `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default 0 or more.
 * `.require_subcommand(min, max)`: Explicitly set min and max allowed subcommands. Setting `max` to 0 is unlimited.
 * `.add_subcommand(name, description="")` Add a subcommand, returns a pointer to the internally stored subcommand.
-* `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line
-* `.get_subcommands()`: The list of subcommands given on the command line
-* `.get_parent()`: Get the parent App or nullptr if called on master App
-* `.parsed()`: True if this subcommand was given on the command line
+* `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line.
+* `.get_subcommands(filter)`: The list of subcommands given on the command line.
+* `.get_parent()`: Get the parent App or nullptr if called on master App.
+* `.get_option(name)`: Get an option pointer by option name
+* `.get_options(filter)`: Get the list of all defined option pointers (useful for processing the app for custom output formats).
+* `.parse_order()`: Get the list of option pointers in the order they were parsed (including duplicates).
+* `.formatter(fmt)`: Set a formatter, with signature `std::string(const App*, std::string, AppFormatMode)`. See Formatting for more details.
+* `.get_description()`: Access the description.
+* `.parsed()`: True if this subcommand was given on the command line.
+* `.set_name(name)`: Add or change the name.
 * `.set_callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
-* `.allow_extras()`: Do not throw an error if extra arguments are left over
+* `.allow_extras()`: Do not throw an error if extra arguments are left over.
 * `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognised item. It is ideal for allowing your app or subcommand to be a "prefix" to calling another app.
 * `.set_footer(message)`: Set text to appear at the bottom of the help string.
+* `.set_help_flag(name, message)`: Set the help flag name and message, returns a pointer to the created option.
+* `.set_help_all_flag(name, message)`: Set the help all flag name and message, returns a pointer to the created option. Expands subcommands.
 * `.set_failure_message(func)`: Set the failure message function. Two provided: `CLI::FailureMessage::help` and `CLI::FailureMessage::simple` (the default).
 * `.group(name)`: Set a group name, defaults to `"Subcommands"`. Setting `""` will be hide the subcommand.
 
-> Note: if you have a fixed number of required positional options, that will match before subcommand names.
+> Note: if you have a fixed number of required positional options, that will match before subcommand names. `{}` is an empty filter function.
 
 ## Configuration file
 
@@ -263,7 +274,7 @@ app.set_config(option_name="",
                required=false)
 ```
 
-If this is called with no arguments, it will remove the configuration file option (like `set_help_flag`). Setting a configuration option is special. If it is present, it will be read along with the normal command line arguments. The file will be read if it exists, and does not throw an error unless `required` is `true`. Configuration files are in `ini` format. An example of a file:
+If this is called with no arguments, it will remove the configuration file option (like `set_help_flag`). Setting a configuration option is special. If it is present, it will be read along with the normal command line arguments. The file will be read if it exists, and does not throw an error unless `required` is `true`. Configuration files are in `ini` format by default (other formats can be added by an adept user). An example of a file:
 
 ```ini
 ; Commments are supported, using a ;
@@ -295,11 +306,20 @@ app.option_defaults()->required();
 
 The default settings for options are inherited to subcommands, as well.
 
+## Formatting
+
+The job of formatting help printouts is delegated to a formatter callable object on Apps and Options. You are free to replace either formatter by calling `formatter(fmt)` on an `App`, where fmt is any copyable callable with the correct signature. 
+CLI11 comes with a default App formatter functional, `Formatter`. It is customizable; you can set `label(key, value)` to replace the default labels like `REQUIRED`, and `column_width(n)` to set the width of the columns before you add the functional to the app or option. You can also override almost any stage of the formatting process in a subclass of either formatter. If you want to make a new formatter from scratch, you can do
+that too; you just need to implement the correct signature. The first argument is a const pointer to the in question. The formatter will get a `std::string` usage name as the second option, and a `AppFormatMode` mode for the final option. It should return a `std::string`.
+
+The `AppFormatMode` can be `Normal`, `All`, or `Sub`, and it indicates the situation the help was called in. `Sub` is optional, but the default formatter uses it to make sure expanded subcommands are called with
+their own formatter since you can't access anything but the call operator once a formatter has been set.
+
 ## Subclassing
 
-The App class was designed allow toolkits to subclass it, to provide preset default options (see above) and setup/teardown code. Subcommands remain an unsubclassed `App`, since those are not expected to need setup and teardown. The default `App` only adds a help flag, `-h,--help`, than can removed/replaced using `.set_help_flag(name, help_string)`. You can remove options if you have pointers to them using `.remove_option(opt)`. You can add a `pre_callback` override to customize the after parse
+The App class was designed allow toolkits to subclass it, to provide preset default options (see above) and setup/teardown code. Subcommands remain an unsubclassed `App`, since those are not expected to need setup and teardown. The default `App` only adds a help flag, `-h,--help`, than can removed/replaced using `.set_help_flag(name, help_string)`. You can also set a help-all flag with `.set_help_all_flag(name, help_string)`; this will expand the subcommands (one level only). You can remove options if you have pointers to them using `.remove_option(opt)`. You can add a `pre_callback` override to customize the after parse
 but before run behavior, while
-still giving the user freedom to `set_callback` on the main app.  
+still giving the user freedom to `set_callback` on the main app.
 
 The most important parse function is `parse(std::vector<std::string>)`, which takes a reversed list of arguments (so that `pop_back` processes the args in the correct order). `get_help_ptr` and `get_config_ptr` give you access to the help/config option pointers. The standard `parse` manually sets the name from the first argument, so it should not be in this vector.
 
@@ -440,11 +460,14 @@ CLI11 was developed at the [University of Cincinnati] to support of the [GooFit]
 [Clara]:                 https://github.com/philsquared/Clara
 [Version 1.0 post]:      https://iscinumpy.gitlab.io/post/announcing-cli11-10/
 [Version 1.3 post]:      https://iscinumpy.gitlab.io/post/announcing-cli11-13/
-[wandbox-online]:        https://img.shields.io/badge/try%20it-online-orange.svg
-[wandbox-link]:          https://wandbox.org/permlink/3a2C5qg6vUjSSpr7
+[wandbox-badge]:         https://img.shields.io/badge/try-online-blue.svg
+[wandbox-link]:          https://wandbox.org/permlink/Z4uwGhnhD2wm2r7Z
 [releases-badge]:        https://img.shields.io/github/release/CLIUtils/CLI11.svg
 [cli11-po-compare]:      https://iscinumpy.gitlab.io/post/comparing-cli11-and-boostpo/
 [DIANA slides]:          https://indico.cern.ch/event/619465/contributions/2507949/attachments/1448567/2232649/20170424-diana-2.pdf
 [Awesome C++]:           https://github.com/fffaraz/awesome-cpp/blob/master/README.md#cli
 [CLI]:                   https://codesynthesis.com/projects/cli/
 [Single file libs]:      https://github.com/nothings/single_file_libs/blob/master/README.md
+[codacy-badge]:          https://api.codacy.com/project/badge/Grade/ac0df3aead2a4421b02070c3f324a0b9
+[codacy-link]:           https://www.codacy.com/app/henryiii/CLI11?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=CLIUtils/CLI11&amp;utm_campaign=Badge_Grade
+[Hunter]:                https://docs.hunter.sh/en/latest/packages/pkg/CLI11.html
diff --git a/packages/CLI11/cmake/AddGoogletest.cmake b/packages/CLI11/cmake/AddGoogletest.cmake
index e6bf162ae678e462c141b6c21988f509b1120d88..fb603e2699a7aeab91ae17037759264aff63c276 100644
--- a/packages/CLI11/cmake/AddGoogletest.cmake
+++ b/packages/CLI11/cmake/AddGoogletest.cmake
@@ -7,48 +7,8 @@
 set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
 set(BUILD_SHARED_LIBS OFF)
 
-if(CMAKE_VERSION VERSION_LESS 3.11)
-    set(UPDATE_DISCONNECTED_IF_AVAILABLE "UPDATE_DISCONNECTED 1")
-    include(DownloadProject)
-    download_project(PROJ                googletest
-                     GIT_REPOSITORY      https://github.com/google/googletest.git
-                     GIT_TAG             release-1.8.0
-                     UPDATE_DISCONNECTED 1
-                     QUIET
-    )
-    
-    # CMake warning suppression will not be needed in version 1.9
-    set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "")
-    add_subdirectory(${googletest_SOURCE_DIR} ${googletest_SOURCE_DIR} EXCLUDE_FROM_ALL)
-else()
-    include(FetchContent)
-    FetchContent_Declare(googletest
-        GIT_REPOSITORY      https://github.com/google/googletest.git
-        GIT_TAG             release-1.8.0)
-    FetchContent_GetProperties(googletest)
-    if(NOT googletest_POPULATED)
-        FetchContent_Populate(googletest)
-        set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "")
-        add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL)
-    endif()
-endif()
-
-
-
-if(CMAKE_CONFIGURATION_TYPES)
-    add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} 
-        --force-new-ctest-process --output-on-failure 
-        --build-config "$<CONFIGURATION>")
-else()
-    add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} 
-        --force-new-ctest-process --output-on-failure)
-endif()
-set_target_properties(check PROPERTIES FOLDER "Scripts")
-
-#include_directories(${gtest_SOURCE_DIR}/include)
-
-# More modern way to do the last line, less messy but needs newish CMake:
-# target_include_directories(gtest INTERFACE ${gtest_SOURCE_DIR}/include)
+set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "")
+add_subdirectory("${CLI11_SOURCE_DIR}/extern/googletest" "${CLI11_BINARY_DIR}/extern/googletest" EXCLUDE_FROM_ALL)
 
 
 if(GOOGLE_TEST_INDIVIDUAL)
diff --git a/packages/CLI11/cmake/CodeCoverage.cmake b/packages/CLI11/cmake/CodeCoverage.cmake
index 879e8e59c9d4302843dbf098980f9be1237be6d8..a1b7b86074b8ab1cf6b16665fce4c53541b54ed8 100644
--- a/packages/CLI11/cmake/CodeCoverage.cmake
+++ b/packages/CLI11/cmake/CodeCoverage.cmake
@@ -1,4 +1,4 @@
-# Copyright (c) 2012 - 2015, Lars Bilke
+# Copyright (c) 2012 - 2017, Lars Bilke
 # All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without modification,
@@ -26,7 +26,7 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #
-#
+# CHANGES:
 #
 # 2012-01-31, Lars Bilke
 # - Enable Code Coverage
@@ -35,164 +35,210 @@
 # - Added support for Clang.
 # - Some additional usage instructions.
 #
+# 2016-02-03, Lars Bilke
+# - Refactored functions to use named parameters
+#
+# 2017-06-02, Lars Bilke
+# - Merged with modified version from github.com/ufz/ogs
+#
+#
 # USAGE:
-
-# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here:
-#      http://stackoverflow.com/a/22404544/80480
 #
 # 1. Copy this file into your cmake modules path.
 #
 # 2. Add the following line to your CMakeLists.txt:
-#      INCLUDE(CodeCoverage)
+#      include(CodeCoverage)
 #
-# 3. Set compiler flags to turn off optimization and enable coverage:
-#    SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
-#	 SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
+# 3. Append necessary compiler flags:
+#      APPEND_COVERAGE_COMPILER_FLAGS()
 #
-# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
-#    which runs your test executable and produces a lcov code coverage report:
+# 4. If you need to exclude additional directories from the report, specify them
+#    using the COVERAGE_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE.
 #    Example:
-#	 SETUP_TARGET_FOR_COVERAGE(
-#				my_coverage_target  # Name for custom target.
-#				test_driver         # Name of the test driver executable that runs the tests.
-#									# NOTE! This should always have a ZERO as exit code
-#									# otherwise the coverage generation will not complete.
-#				coverage            # Name of output directory.
-#				)
+#      set(COVERAGE_EXCLUDES 'dir1/*' 'dir2/*')
 #
-# 4. Build a Debug build:
-#	 cmake -DCMAKE_BUILD_TYPE=Debug ..
-#	 make
-#	 make my_coverage_target
+# 5. Use the functions described below to create a custom make target which
+#    runs your test executable and produces a code coverage report.
 #
+# 6. Build a Debug build:
+#      cmake -DCMAKE_BUILD_TYPE=Debug ..
+#      make
+#      make my_coverage_target
 #
 
-# Check prereqs
+include(CMakeParseArguments)
 
-FIND_PROGRAM( GCOV_PATH gcov)
-FIND_PROGRAM( LCOV_PATH lcov )
-FIND_PROGRAM( GENHTML_PATH genhtml )
-FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
-
-IF(NOT GCOV_PATH)
-	MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
-ENDIF() # NOT GCOV_PATH
-
-IF("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
-	IF("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
-		MESSAGE(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
-	ENDIF()
-ELSEIF(NOT CMAKE_COMPILER_IS_GNUCXX)
-	MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
-ENDIF() # CHECK VALID COMPILER
-
-SET(CMAKE_CXX_FLAGS_COVERAGE
-    "-g -O0 --coverage"
+# Check prereqs
+find_program( GCOV_PATH gcov )
+find_program( LCOV_PATH  NAMES lcov lcov.bat lcov.exe lcov.perl)
+find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
+find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
+find_program( SIMPLE_PYTHON_EXECUTABLE python )
+
+if(NOT GCOV_PATH)
+    message(FATAL_ERROR "gcov not found! Aborting...")
+endif() # NOT GCOV_PATH
+
+if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
+    if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
+        message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
+    endif()
+elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
+    message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
+endif()
+
+set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage"
+    CACHE INTERNAL "")
+
+set(CMAKE_CXX_FLAGS_COVERAGE
+    ${COVERAGE_COMPILER_FLAGS}
     CACHE STRING "Flags used by the C++ compiler during coverage builds."
     FORCE )
-SET(CMAKE_C_FLAGS_COVERAGE
-    "-g -O0 --coverage"
+set(CMAKE_C_FLAGS_COVERAGE
+    ${COVERAGE_COMPILER_FLAGS}
     CACHE STRING "Flags used by the C compiler during coverage builds."
     FORCE )
-SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
-    "--coverage"
+set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
+    ""
     CACHE STRING "Flags used for linking binaries during coverage builds."
     FORCE )
-SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
-    "--coverage"
+set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
+    ""
     CACHE STRING "Flags used by the shared libraries linker during coverage builds."
     FORCE )
-MARK_AS_ADVANCED(
+mark_as_advanced(
     CMAKE_CXX_FLAGS_COVERAGE
     CMAKE_C_FLAGS_COVERAGE
     CMAKE_EXE_LINKER_FLAGS_COVERAGE
     CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
 
-IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
-  MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
-ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
-
-
-# Param _targetname     The name of new the custom make target
-# Param _testrunner     The name of the target which runs the tests.
-#						MUST return ZERO always, even on errors.
-#						If not, no coverage report will be created!
-# Param _outputname     lcov output is generated as _outputname.info
-#                       HTML report is generated in _outputname/index.html
-# Optional fourth parameter is passed as arguments to _testrunner
-#   Pass them in list form, e.g.: "-j;2" for -j 2
-FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
-
-	IF(NOT LCOV_PATH)
-		MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
-	ENDIF() # NOT LCOV_PATH
-
-	IF(NOT GENHTML_PATH)
-		MESSAGE(FATAL_ERROR "genhtml not found! Aborting...")
-	ENDIF() # NOT GENHTML_PATH
-
-	SET(coverage_info "${CMAKE_BINARY_DIR}/${_outputname}.info")
-	SET(coverage_cleaned "${coverage_info}.cleaned")
-
-	SEPARATE_ARGUMENTS(test_command UNIX_COMMAND "${_testrunner}")
-
-	# Setup target
-	ADD_CUSTOM_TARGET(${_targetname}
-
-		# Cleanup lcov
-		${LCOV_PATH} --directory . --zerocounters
-
-		# Run tests
-		COMMAND ${test_command} ${ARGV3}
-
-		# Capturing lcov counters and generating report
-		COMMAND ${LCOV_PATH} --directory . --capture --output-file ${coverage_info}
-		COMMAND ${LCOV_PATH} --remove ${coverage_info} '*/tests/*' '*gtest*' '*gmock*' '/usr/*' --output-file ${coverage_cleaned}
-		COMMAND ${GENHTML_PATH} -o ${_outputname} ${coverage_cleaned}
-		COMMAND ${CMAKE_COMMAND} -E remove ${coverage_info} ${coverage_cleaned}
-
-		WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-		COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
-	)
-
-	# Show info where to find the report
-	ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
-		COMMAND ;
-		COMMENT "Open ./${_outputname}/index.html in your browser to view the coverage report."
-	)
-
-ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE
-
-# Param _targetname     The name of new the custom make target
-# Param _testrunner     The name of the target which runs the tests
-# Param _outputname     cobertura output is generated as _outputname.xml
-# Optional fourth parameter is passed as arguments to _testrunner
-#   Pass them in list form, e.g.: "-j;2" for -j 2
-FUNCTION(SETUP_TARGET_FOR_COVERAGE_COBERTURA _targetname _testrunner _outputname)
-
-	IF(NOT PYTHON_EXECUTABLE)
-		MESSAGE(FATAL_ERROR "Python not found! Aborting...")
-	ENDIF() # NOT PYTHON_EXECUTABLE
-
-	IF(NOT GCOVR_PATH)
-		MESSAGE(FATAL_ERROR "gcovr not found! Aborting...")
-	ENDIF() # NOT GCOVR_PATH
-
-	ADD_CUSTOM_TARGET(${_targetname}
-
-		# Run tests
-		${_testrunner} ${ARGV3}
-
-		# Running gcovr
-		COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} -e '${CMAKE_SOURCE_DIR}/tests/'  -o ${_outputname}.xml
-		WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
-		COMMENT "Running gcovr to produce Cobertura code coverage report."
-	)
-
-	# Show info where to find the report
-	ADD_CUSTOM_COMMAND(TARGET ${_targetname} POST_BUILD
-		COMMAND ;
-		COMMENT "Cobertura code coverage report saved in ${_outputname}.xml."
-	)
-
-ENDFUNCTION() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
+if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+    message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
+endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
+
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    link_libraries(gcov)
+else()
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
+endif()
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# SETUP_TARGET_FOR_COVERAGE(
+#     NAME testrunner_coverage                    # New target name
+#     EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+#     DEPENDENCIES testrunner                     # Dependencies to build first
+# )
+function(SETUP_TARGET_FOR_COVERAGE)
+
+    set(options NONE)
+    set(oneValueArgs NAME)
+    set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+    cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    if(NOT LCOV_PATH)
+        message(FATAL_ERROR "lcov not found! Aborting...")
+    endif() # NOT LCOV_PATH
+
+    if(NOT GENHTML_PATH)
+        message(FATAL_ERROR "genhtml not found! Aborting...")
+    endif() # NOT GENHTML_PATH
+
+    # Setup target
+    add_custom_target(${Coverage_NAME}
+
+        # Cleanup lcov
+        COMMAND ${LCOV_PATH} --directory . --zerocounters
+        # Create baseline to make sure untouched files show up in the report
+        COMMAND ${LCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base
+
+        # Run tests
+        COMMAND ${Coverage_EXECUTABLE}
+
+        # Capturing lcov counters and generating report
+        COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info
+        # add baseline counters
+        COMMAND ${LCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total
+        COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+        COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+        COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+
+        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+        DEPENDS ${Coverage_DEPENDENCIES}
+        COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
+    )
+    
+    # Show where to find the lcov info report
+    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+        COMMAND ;
+        COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
+    )
+
+    # Show info where to find the report
+    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+        COMMAND ;
+        COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
+    )
+
+endfunction() # SETUP_TARGET_FOR_COVERAGE
+
+# Defines a target for running and collection code coverage information
+# Builds dependencies, runs the given executable and outputs reports.
+# NOTE! The executable should always have a ZERO as exit code otherwise
+# the coverage generation will not complete.
+#
+# SETUP_TARGET_FOR_COVERAGE_COBERTURA(
+#     NAME ctest_coverage                    # New target name
+#     EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
+#     DEPENDENCIES executable_target         # Dependencies to build first
+# )
+function(SETUP_TARGET_FOR_COVERAGE_COBERTURA)
+
+    set(options NONE)
+    set(oneValueArgs NAME)
+    set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+    cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+    if(NOT SIMPLE_PYTHON_EXECUTABLE)
+        message(FATAL_ERROR "python not found! Aborting...")
+    endif() # NOT SIMPLE_PYTHON_EXECUTABLE
+
+    if(NOT GCOVR_PATH)
+        message(FATAL_ERROR "gcovr not found! Aborting...")
+    endif() # NOT GCOVR_PATH
+
+    # Combine excludes to several -e arguments
+    set(COBERTURA_EXCLUDES "")
+    foreach(EXCLUDE ${COVERAGE_EXCLUDES})
+        set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}")
+    endforeach()
+
+    add_custom_target(${Coverage_NAME}
+
+        # Run tests
+        ${Coverage_EXECUTABLE}
+
+        # Running gcovr
+        COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} ${COBERTURA_EXCLUDES}
+            -o ${Coverage_NAME}.xml
+        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+        DEPENDS ${Coverage_DEPENDENCIES}
+        COMMENT "Running gcovr to produce Cobertura code coverage report."
+    )
+
+    # Show info where to find the report
+    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
+        COMMAND ;
+        COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
+    )
+
+endfunction() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
+
+function(APPEND_COVERAGE_COMPILER_FLAGS)
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
+    message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
+endfunction() # APPEND_COVERAGE_COMPILER_FLAGS
diff --git a/packages/CLI11/cmake/DownloadProject.CMakeLists.cmake.in b/packages/CLI11/cmake/DownloadProject.CMakeLists.cmake.in
deleted file mode 100644
index 89be4fdd4f2e0db8d22c47d4d3f8c664078c7768..0000000000000000000000000000000000000000
--- a/packages/CLI11/cmake/DownloadProject.CMakeLists.cmake.in
+++ /dev/null
@@ -1,17 +0,0 @@
-# Distributed under the OSI-approved MIT License.  See accompanying
-# file LICENSE or https://github.com/Crascit/DownloadProject for details.
-
-cmake_minimum_required(VERSION 2.8.2)
-
-project(${DL_ARGS_PROJ}-download NONE)
-
-include(ExternalProject)
-ExternalProject_Add(${DL_ARGS_PROJ}-download
-                    ${DL_ARGS_UNPARSED_ARGUMENTS}
-                    SOURCE_DIR          "${DL_ARGS_SOURCE_DIR}"
-                    BINARY_DIR          "${DL_ARGS_BINARY_DIR}"
-                    CONFIGURE_COMMAND   ""
-                    BUILD_COMMAND       ""
-                    INSTALL_COMMAND     ""
-                    TEST_COMMAND        ""
-)
diff --git a/packages/CLI11/cmake/DownloadProject.cmake b/packages/CLI11/cmake/DownloadProject.cmake
deleted file mode 100644
index 798c74b6380c7bffe8cce235ac7d64d609a34c4d..0000000000000000000000000000000000000000
--- a/packages/CLI11/cmake/DownloadProject.cmake
+++ /dev/null
@@ -1,164 +0,0 @@
-# Distributed under the OSI-approved MIT License.  See accompanying
-# file LICENSE or https://github.com/Crascit/DownloadProject for details.
-#
-# MODULE:   DownloadProject
-#
-# PROVIDES:
-#   download_project( PROJ projectName
-#                    [PREFIX prefixDir]
-#                    [DOWNLOAD_DIR downloadDir]
-#                    [SOURCE_DIR srcDir]
-#                    [BINARY_DIR binDir]
-#                    [QUIET]
-#                    ...
-#   )
-#
-#       Provides the ability to download and unpack a tarball, zip file, git repository,
-#       etc. at configure time (i.e. when the cmake command is run). How the downloaded
-#       and unpacked contents are used is up to the caller, but the motivating case is
-#       to download source code which can then be included directly in the build with
-#       add_subdirectory() after the call to download_project(). Source and build
-#       directories are set up with this in mind.
-#
-#       The PROJ argument is required. The projectName value will be used to construct
-#       the following variables upon exit (obviously replace projectName with its actual
-#       value):
-#
-#           projectName_SOURCE_DIR
-#           projectName_BINARY_DIR
-#
-#       The SOURCE_DIR and BINARY_DIR arguments are optional and would not typically
-#       need to be provided. They can be specified if you want the downloaded source
-#       and build directories to be located in a specific place. The contents of
-#       projectName_SOURCE_DIR and projectName_BINARY_DIR will be populated with the
-#       locations used whether you provide SOURCE_DIR/BINARY_DIR or not.
-#
-#       The DOWNLOAD_DIR argument does not normally need to be set. It controls the
-#       location of the temporary CMake build used to perform the download.
-#
-#       The PREFIX argument can be provided to change the base location of the default
-#       values of DOWNLOAD_DIR, SOURCE_DIR and BINARY_DIR. If all of those three arguments
-#       are provided, then PREFIX will have no effect. The default value for PREFIX is
-#       CMAKE_BINARY_DIR.
-#
-#       The QUIET option can be given if you do not want to show the output associated
-#       with downloading the specified project.
-#
-#       In addition to the above, any other options are passed through unmodified to
-#       ExternalProject_Add() to perform the actual download, patch and update steps.
-#       The following ExternalProject_Add() options are explicitly prohibited (they
-#       are reserved for use by the download_project() command):
-#
-#           CONFIGURE_COMMAND
-#           BUILD_COMMAND
-#           INSTALL_COMMAND
-#           TEST_COMMAND
-#
-#       Only those ExternalProject_Add() arguments which relate to downloading, patching
-#       and updating of the project sources are intended to be used. Also note that at
-#       least one set of download-related arguments are required.
-#
-#       If using CMake 3.2 or later, the UPDATE_DISCONNECTED option can be used to
-#       prevent a check at the remote end for changes every time CMake is run
-#       after the first successful download. See the documentation of the ExternalProject
-#       module for more information. It is likely you will want to use this option if it
-#       is available to you. Note, however, that the ExternalProject implementation contains
-#       bugs which result in incorrect handling of the UPDATE_DISCONNECTED option when
-#       using the URL download method or when specifying a SOURCE_DIR with no download
-#       method. Fixes for these have been created, the last of which is scheduled for
-#       inclusion in CMake 3.8.0. Details can be found here:
-#
-#           https://gitlab.kitware.com/cmake/cmake/commit/bdca68388bd57f8302d3c1d83d691034b7ffa70c
-#           https://gitlab.kitware.com/cmake/cmake/issues/16428
-#
-#       If you experience build errors related to the update step, consider avoiding
-#       the use of UPDATE_DISCONNECTED.
-#
-# EXAMPLE USAGE:
-#
-#   include(DownloadProject)
-#   download_project(PROJ                googletest
-#                    GIT_REPOSITORY      https://github.com/google/googletest.git
-#                    GIT_TAG             master
-#                    UPDATE_DISCONNECTED 1
-#                    QUIET
-#   )
-#
-#   add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR})
-#
-#========================================================================================
-
-
-set(_DownloadProjectDir "${CMAKE_CURRENT_LIST_DIR}")
-
-include(CMakeParseArguments)
-
-function(download_project)
-
-    set(options QUIET)
-    set(oneValueArgs
-        PROJ
-        PREFIX
-        DOWNLOAD_DIR
-        SOURCE_DIR
-        BINARY_DIR
-        # Prevent the following from being passed through
-        CONFIGURE_COMMAND
-        BUILD_COMMAND
-        INSTALL_COMMAND
-        TEST_COMMAND
-    )
-    set(multiValueArgs "")
-
-    cmake_parse_arguments(DL_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
-    # Hide output if requested
-    if (DL_ARGS_QUIET)
-        set(OUTPUT_QUIET "OUTPUT_QUIET")
-    else()
-        unset(OUTPUT_QUIET)
-        message(STATUS "Downloading/updating ${DL_ARGS_PROJ}")
-    endif()
-
-    # Set up where we will put our temporary CMakeLists.txt file and also
-    # the base point below which the default source and binary dirs will be
-    if (NOT DL_ARGS_PREFIX)
-        set(DL_ARGS_PREFIX "${CMAKE_BINARY_DIR}")
-    endif()
-    if (NOT DL_ARGS_DOWNLOAD_DIR)
-        set(DL_ARGS_DOWNLOAD_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-download")
-    endif()
-
-    # Ensure the caller can know where to find the source and build directories
-    if (NOT DL_ARGS_SOURCE_DIR)
-        set(DL_ARGS_SOURCE_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-src")
-    endif()
-    if (NOT DL_ARGS_BINARY_DIR)
-        set(DL_ARGS_BINARY_DIR "${DL_ARGS_PREFIX}/${DL_ARGS_PROJ}-build")
-    endif()
-    set(${DL_ARGS_PROJ}_SOURCE_DIR "${DL_ARGS_SOURCE_DIR}" PARENT_SCOPE)
-    set(${DL_ARGS_PROJ}_BINARY_DIR "${DL_ARGS_BINARY_DIR}" PARENT_SCOPE)
-
-    # Create and build a separate CMake project to carry out the download.
-    # If we've already previously done these steps, they will not cause
-    # anything to be updated, so extra rebuilds of the project won't occur.
-    configure_file("${_DownloadProjectDir}/DownloadProject.CMakeLists.cmake.in"
-                   "${DL_ARGS_DOWNLOAD_DIR}/CMakeLists.txt")
-    execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
-                    RESULT_VARIABLE result
-                    ${OUTPUT_QUIET}
-                    WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
-    )
-    if(result)
-        message(FATAL_ERROR "CMake step for ${DL_ARGS_PROJ} failed: ${result}")
-    endif()
-    execute_process(COMMAND ${CMAKE_COMMAND} --build .
-                    RESULT_VARIABLE result
-                    ${OUTPUT_QUIET}
-                    WORKING_DIRECTORY "${DL_ARGS_DOWNLOAD_DIR}"
-    )
-    if(result)
-        message(FATAL_ERROR "Build step for ${DL_ARGS_PROJ} failed: ${result}")
-    endif()
-
-endfunction()
diff --git a/packages/CLI11/conanfile.py b/packages/CLI11/conanfile.py
index 7a32f711da704bcd3ddb5d51e1842739fe2fc3bb..efbaaca2ed47f58f17171d16f3800d3e97160a70 100644
--- a/packages/CLI11/conanfile.py
+++ b/packages/CLI11/conanfile.py
@@ -18,7 +18,7 @@ class HelloConan(ConanFile):
     license = "BSD 3 clause"
     description = "Command Line Interface toolkit for C++11"
 
-    exports_sources = "LICENSE", "README.md", "include/*", "cmake/*", "CMakeLists.txt", "tests/*"
+    exports_sources = "LICENSE", "README.md", "include/*", "extern/*", "cmake/*", "CMakeLists.txt", "tests/*"
 
     def build(self): # this is not building a library, just tests
         cmake = CMake(self)
diff --git a/packages/CLI11/docs/mainpage.md b/packages/CLI11/docs/mainpage.md
index 7f97f62ff4402dcfec37b0e5c62ab6b77e67e439..2c488eb9c0db75b72a44bd51a9d272427f169b53 100644
--- a/packages/CLI11/docs/mainpage.md
+++ b/packages/CLI11/docs/mainpage.md
@@ -8,6 +8,8 @@ The main classes are:
 |---------------|-------------------------------------|
 |CLI::Option    | Options, stored in the app          |
 |CLI::App       | The main application or subcommands |
+|CLI::Validator | A check that can affect the type name |
+|CLI::Formatter | A subclassable formatter for help printing |
 |CLI::ExitCode  | A scoped enum with exit codes       |
 |CLI::Timer     | A timer class, only in CLI/Timer.hpp (not in `CLI11.hpp`) |
 |CLI::AutoTimer | A timer that prints on deletion     |
diff --git a/packages/CLI11/examples/CMakeLists.txt b/packages/CLI11/examples/CMakeLists.txt
index c6851229d4ae826000b1fb3b97a587b3cfa1a141..c4618ca47025a30bddbf4a86007abb1d820b5444 100644
--- a/packages/CLI11/examples/CMakeLists.txt
+++ b/packages/CLI11/examples/CMakeLists.txt
@@ -71,7 +71,7 @@ add_cli_exe(enum enum.cpp)
 add_test(NAME enum_pass COMMAND enum -l 1)
 add_test(NAME enum_fail COMMAND enum -l 4)
 set_property(TEST enum_fail PROPERTY PASS_REGULAR_EXPRESSION
-    "Could not convert: -l,--level = 4")
+    "Could not convert: --level = 4")
 
 add_cli_exe(modhelp modhelp.cpp)
 add_test(NAME modhelp COMMAND modhelp -a test -h)
@@ -83,3 +83,5 @@ add_test(NAME subcom_in_files COMMAND subcommand_main subcommand_a -f this.txt -
 set_property(TEST subcom_in_files PROPERTY PASS_REGULAR_EXPRESSION
     "Working on file: this\.txt"
     "Using foo!")
+
+add_cli_exe(formatter formatter.cpp)
diff --git a/packages/CLI11/examples/enum.cpp b/packages/CLI11/examples/enum.cpp
index 7018681faa76252585e2b249c810a6d8facba606..3e841da6a70b46ddff5a64c382fafdefb8b8344f 100644
--- a/packages/CLI11/examples/enum.cpp
+++ b/packages/CLI11/examples/enum.cpp
@@ -1,5 +1,5 @@
-#include <sstream>
 #include <CLI/CLI.hpp>
+#include <sstream>
 
 enum class Level : int { High, Medium, Low };
 
diff --git a/packages/CLI11/examples/formatter.cpp b/packages/CLI11/examples/formatter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d67ec6126620435a023b7a94d4aed24cf584ab5
--- /dev/null
+++ b/packages/CLI11/examples/formatter.cpp
@@ -0,0 +1,29 @@
+#include <CLI/CLI.hpp>
+
+class MyFormatter : public CLI::Formatter {
+  public:
+    MyFormatter() : Formatter() {}
+    std::string make_option_opts(const CLI::Option *) const override { return " OPTION"; }
+};
+
+int main(int argc, char **argv) {
+    CLI::App app;
+    app.set_help_all_flag("--help-all", "Show all help");
+
+    auto fmt = std::make_shared<MyFormatter>();
+    fmt->column_width(15);
+    app.formatter(fmt);
+
+    app.add_flag("--flag", "This is a flag");
+
+    auto sub1 = app.add_subcommand("one", "Description One");
+    sub1->add_flag("--oneflag", "Some flag");
+    auto sub2 = app.add_subcommand("two", "Description Two");
+    sub2->add_flag("--twoflag", "Some other flag");
+
+    CLI11_PARSE(app, argc, argv);
+
+    std::cout << "This app was meant to show off the formatter, run with -h" << std::endl;
+
+    return 0;
+}
diff --git a/packages/CLI11/examples/inter_argument_order.cpp b/packages/CLI11/examples/inter_argument_order.cpp
index 9e3c246b9225ffcea72519e5477160da40e340a5..23f8fec3681dbbf5bd2acef13aca3a025e8b0c6a 100644
--- a/packages/CLI11/examples/inter_argument_order.cpp
+++ b/packages/CLI11/examples/inter_argument_order.cpp
@@ -1,8 +1,8 @@
 #include <CLI/CLI.hpp>
+#include <algorithm>
 #include <iostream>
-#include <vector>
 #include <tuple>
-#include <algorithm>
+#include <vector>
 
 int main(int argc, char **argv) {
     CLI::App app{"An app to practice mixing unlimited arguments, but still recover the original order."};
diff --git a/packages/CLI11/examples/subcommands.cpp b/packages/CLI11/examples/subcommands.cpp
index 8872d3fcd846b87df781186a266ba3f59603f081..580f4d1ace93f3584d690677c7eae590aeba0653 100644
--- a/packages/CLI11/examples/subcommands.cpp
+++ b/packages/CLI11/examples/subcommands.cpp
@@ -3,6 +3,7 @@
 int main(int argc, char **argv) {
 
     CLI::App app("K3Pi goofit fitter");
+    app.set_help_all_flag("--help-all", "Expand all help");
     app.add_flag("--random", "Some random flag");
     CLI::App *start = app.add_subcommand("start", "A great subcommand");
     CLI::App *stop = app.add_subcommand("stop", "Do you really want to stop?");
diff --git a/packages/CLI11/extern/googletest b/packages/CLI11/extern/googletest
new file mode 160000
index 0000000000000000000000000000000000000000..ec44c6c1675c25b9827aacd08c02433cccde7780
--- /dev/null
+++ b/packages/CLI11/extern/googletest
@@ -0,0 +1 @@
+Subproject commit ec44c6c1675c25b9827aacd08c02433cccde7780
diff --git a/packages/CLI11/extern/sanitizers b/packages/CLI11/extern/sanitizers
new file mode 160000
index 0000000000000000000000000000000000000000..6947cff3a9c9305eb9c16135dd81da3feb4bf87f
--- /dev/null
+++ b/packages/CLI11/extern/sanitizers
@@ -0,0 +1 @@
+Subproject commit 6947cff3a9c9305eb9c16135dd81da3feb4bf87f
diff --git a/packages/CLI11/include/CLI/App.hpp b/packages/CLI11/include/CLI/App.hpp
index e2c2391945dc739e06fc81576f7b8a7717af2132..c1d30ec48826604741ee8cb1ce820b81f0971265 100644
--- a/packages/CLI11/include/CLI/App.hpp
+++ b/packages/CLI11/include/CLI/App.hpp
@@ -7,6 +7,7 @@
 #include <deque>
 #include <functional>
 #include <iostream>
+#include <iterator>
 #include <memory>
 #include <numeric>
 #include <set>
@@ -14,11 +15,11 @@
 #include <string>
 #include <utility>
 #include <vector>
-#include <iterator>
 
 // CLI Library includes
+#include "CLI/ConfigFwd.hpp"
 #include "CLI/Error.hpp"
-#include "CLI/Ini.hpp"
+#include "CLI/FormatterFwd.hpp"
 #include "CLI/Macros.hpp"
 #include "CLI/Option.hpp"
 #include "CLI/Split.hpp"
@@ -64,8 +65,8 @@ class App {
     /// @name Basics
     ///@{
 
-    /// Subcommand name or program name (from parser)
-    std::string name_{"program"};
+    /// Subcommand name or program name (from parser if name is empty)
+    std::string name_;
 
     /// Description of the current program/subcommand
     std::string description_;
@@ -74,7 +75,7 @@ class App {
     bool allow_extras_{false};
 
     /// If true, allow extra arguments in the ini file (ie, don't throw an error). INHERITABLE
-    bool allow_ini_extras_{false};
+    bool allow_config_extras_{false};
 
     ///  If true, return immediately on an unrecognised option (implies allow_extras) INHERITABLE
     bool prefix_command_{false};
@@ -102,6 +103,12 @@ class App {
     /// A pointer to the help flag if there is one INHERITABLE
     Option *help_ptr_{nullptr};
 
+    /// A pointer to the help all flag if there is one INHERITABLE
+    Option *help_all_ptr_{nullptr};
+
+    /// This is the formatter for help printing. Default provided. INHERITABLE (same pointer)
+    std::shared_ptr<FormatterBase> formatter_{new Formatter()};
+
     /// The error message printing function INHERITABLE
     std::function<std::string(const App *, const Error &e)> failure_message_ = FailureMessage::simple;
 
@@ -141,7 +148,7 @@ class App {
     /// True if this command/subcommand was parsed
     bool parsed_{false};
 
-    /// Minimum required subcommands
+    /// Minimum required subcommands (not inheritable!)
     size_t require_subcommand_min_ = 0;
 
     /// Max number of subcommands allowed (parsing stops after this number). 0 is unlimited INHERITABLE
@@ -163,14 +170,21 @@ class App {
     /// Pointer to the config option
     Option *config_ptr_{nullptr};
 
+    /// This is the formatter for help printing. Default provided. INHERITABLE (same pointer)
+    std::shared_ptr<Config> config_formatter_{new ConfigINI()};
+
     ///@}
 
     /// Special private constructor for subcommand
-    App(std::string description_, App *parent) : description_(std::move(description_)), parent_(parent) {
+    App(std::string description_, std::string name, App *parent)
+        : name_(std::move(name)), description_(std::move(description_)), parent_(parent) {
         // Inherit if not from a nullptr
         if(parent_ != nullptr) {
             if(parent_->help_ptr_ != nullptr)
-                set_help_flag(parent_->help_ptr_->get_name(), parent_->help_ptr_->get_description());
+                set_help_flag(parent_->help_ptr_->get_name(false, true), parent_->help_ptr_->get_description());
+            if(parent_->help_all_ptr_ != nullptr)
+                set_help_all_flag(parent_->help_all_ptr_->get_name(false, true),
+                                  parent_->help_all_ptr_->get_description());
 
             /// OptionDefaults
             option_defaults_ = parent_->option_defaults_;
@@ -178,12 +192,14 @@ class App {
             // INHERITABLE
             failure_message_ = parent_->failure_message_;
             allow_extras_ = parent_->allow_extras_;
-            allow_ini_extras_ = parent_->allow_ini_extras_;
+            allow_config_extras_ = parent_->allow_config_extras_;
             prefix_command_ = parent_->prefix_command_;
             ignore_case_ = parent_->ignore_case_;
             fallthrough_ = parent_->fallthrough_;
             group_ = parent_->group_;
             footer_ = parent_->footer_;
+            formatter_ = parent_->formatter_;
+            config_formatter_ = parent_->config_formatter_;
             require_subcommand_max_ = parent_->require_subcommand_max_;
         }
     }
@@ -193,7 +209,7 @@ class App {
     ///@{
 
     /// Create a new program. Pass in the same arguments as main(), along with a help string.
-    App(std::string description_ = "") : App(description_, nullptr) {
+    explicit App(std::string description_ = "", std::string name = "") : App(description_, name, nullptr) {
         set_help_flag("-h,--help", "Print this help message and exit");
     }
 
@@ -211,6 +227,12 @@ class App {
         return this;
     }
 
+    /// Set a name for the app (empty will use parser to set the name)
+    App *set_name(std::string name = "") {
+        name_ = name;
+        return this;
+    }
+
     /// Remove the error when extras are left over on the command line.
     App *allow_extras(bool allow = true) {
         allow_extras_ = allow;
@@ -219,9 +241,9 @@ class App {
 
     /// Remove the error when extras are left over on the command line.
     /// Will also call App::allow_extras().
-    App *allow_ini_extras(bool allow = true) {
+    App *allow_config_extras(bool allow = true) {
         allow_extras(allow);
-        allow_ini_extras_ = allow;
+        allow_config_extras_ = allow;
         return this;
     }
 
@@ -243,6 +265,24 @@ class App {
         return this;
     }
 
+    /// Set the help formatter
+    App *formatter(std::shared_ptr<FormatterBase> fmt) {
+        formatter_ = fmt;
+        return this;
+    }
+
+    /// Set the help formatter
+    App *formatter_fn(std::function<std::string(const App *, std::string, AppFormatMode)> fmt) {
+        formatter_ = std::make_shared<FormatterLambda>(fmt);
+        return this;
+    }
+
+    /// Set the config formatter
+    App *config_formatter(std::shared_ptr<Config> fmt) {
+        config_formatter_ = fmt;
+        return this;
+    }
+
     /// Check to see if this subcommand was parsed, true only if received on command line.
     bool parsed() const { return parsed_; }
 
@@ -288,10 +328,7 @@ class App {
                        T &variable, ///< The variable to set
                        std::string description = "") {
 
-        std::string simple_name = CLI::detail::split(name, ',').at(0);
-        CLI::callback_t fun = [&variable, simple_name](CLI::results_t res) {
-            return detail::lexical_cast(res[0], variable);
-        };
+        CLI::callback_t fun = [&variable](CLI::results_t res) { return detail::lexical_cast(res[0], variable); };
 
         Option *opt = add_option(name, fun, description, false);
         opt->set_custom_option(detail::type_name<T>());
@@ -305,10 +342,7 @@ class App {
                        std::string description,
                        bool defaulted) {
 
-        std::string simple_name = CLI::detail::split(name, ',').at(0);
-        CLI::callback_t fun = [&variable, simple_name](CLI::results_t res) {
-            return detail::lexical_cast(res[0], variable);
-        };
+        CLI::callback_t fun = [&variable](CLI::results_t res) { return detail::lexical_cast(res[0], variable); };
 
         Option *opt = add_option(name, fun, description, defaulted);
         opt->set_custom_option(detail::type_name<T>());
@@ -365,7 +399,7 @@ class App {
         return opt;
     }
 
-    /// Set a help flag, replaced the existing one if present
+    /// Set a help flag, replace the existing one if present
     Option *set_help_flag(std::string name = "", std::string description = "") {
         if(help_ptr_ != nullptr) {
             remove_option(help_ptr_);
@@ -374,13 +408,31 @@ class App {
 
         // Empty name will simply remove the help flag
         if(!name.empty()) {
-            help_ptr_ = add_flag(name, description);
+            help_ptr_ = add_flag_function(name, [](size_t) -> void { throw CallForHelp(); }, description);
+            help_ptr_->short_circuit(true);
             help_ptr_->configurable(false);
         }
 
         return help_ptr_;
     }
 
+    /// Set a help all flag, replaced the existing one if present
+    Option *set_help_all_flag(std::string name = "", std::string description = "") {
+        if(help_all_ptr_ != nullptr) {
+            remove_option(help_all_ptr_);
+            help_all_ptr_ = nullptr;
+        }
+
+        // Empty name will simply remove the help all flag
+        if(!name.empty()) {
+            help_all_ptr_ = add_flag_function(name, [](size_t) -> void { throw CallForAllHelp(); }, description);
+            help_all_ptr_->short_circuit(true);
+            help_all_ptr_->configurable(false);
+        }
+
+        return help_all_ptr_;
+    }
+
     /// Add option for flag
     Option *add_flag(std::string name, std::string description = "") {
         CLI::callback_t fun = [](CLI::results_t) { return true; };
@@ -460,11 +512,11 @@ class App {
     }
 #endif
 
-    /// Add set of options (No default)
+    /// Add set of options (No default, temp refernce, such as an inline set)
     template <typename T>
     Option *add_set(std::string name,
-                    T &member,           ///< The selected member of the set
-                    std::set<T> options, ///< The set of possibilities
+                    T &member,                   ///< The selected member of the set
+                    const std::set<T> &&options, ///< The set of possibilities
                     std::string description = "") {
 
         std::string simple_name = CLI::detail::split(name, ',').at(0);
@@ -482,11 +534,33 @@ class App {
         return opt;
     }
 
-    /// Add set of options
+    /// Add set of options (No default, non-temp refernce, such as an existing set)
     template <typename T>
     Option *add_set(std::string name,
-                    T &member,           ///< The selected member of the set
-                    std::set<T> options, ///< The set of posibilities
+                    T &member,                  ///< The selected member of the set
+                    const std::set<T> &options, ///< The set of possibilities
+                    std::string description = "") {
+
+        std::string simple_name = CLI::detail::split(name, ',').at(0);
+        CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
+            bool retval = detail::lexical_cast(res[0], member);
+            if(!retval)
+                throw ConversionError(res[0], simple_name);
+            return std::find(std::begin(options), std::end(options), member) != std::end(options);
+        };
+
+        Option *opt = add_option(name, fun, description, false);
+        opt->set_type_name_fn(
+            [&options]() { return std::string(detail::type_name<T>()) + " in {" + detail::join(options) + "}"; });
+
+        return opt;
+    }
+
+    /// Add set of options (with default, R value, such as an inline set)
+    template <typename T>
+    Option *add_set(std::string name,
+                    T &member,                   ///< The selected member of the set
+                    const std::set<T> &&options, ///< The set of posibilities
                     std::string description,
                     bool defaulted) {
 
@@ -510,10 +584,37 @@ class App {
         return opt;
     }
 
-    /// Add set of options, string only, ignore case (no default)
+    /// Add set of options (with default, L value refernce, such as an existing set)
+    template <typename T>
+    Option *add_set(std::string name,
+                    T &member,                  ///< The selected member of the set
+                    const std::set<T> &options, ///< The set of posibilities
+                    std::string description,
+                    bool defaulted) {
+
+        std::string simple_name = CLI::detail::split(name, ',').at(0);
+        CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
+            bool retval = detail::lexical_cast(res[0], member);
+            if(!retval)
+                throw ConversionError(res[0], simple_name);
+            return std::find(std::begin(options), std::end(options), member) != std::end(options);
+        };
+
+        Option *opt = add_option(name, fun, description, defaulted);
+        opt->set_type_name_fn(
+            [&options]() { return std::string(detail::type_name<T>()) + " in {" + detail::join(options) + "}"; });
+        if(defaulted) {
+            std::stringstream out;
+            out << member;
+            opt->set_default_str(out.str());
+        }
+        return opt;
+    }
+
+    /// Add set of options, string only, ignore case (no default, R value)
     Option *add_set_ignore_case(std::string name,
-                                std::string &member,           ///< The selected member of the set
-                                std::set<std::string> options, ///< The set of possibilities
+                                std::string &member,                   ///< The selected member of the set
+                                const std::set<std::string> &&options, ///< The set of possibilities
                                 std::string description = "") {
 
         std::string simple_name = CLI::detail::split(name, ',').at(0);
@@ -538,10 +639,38 @@ class App {
         return opt;
     }
 
-    /// Add set of options, string only, ignore case
+    /// Add set of options, string only, ignore case (no default, L value)
+    Option *add_set_ignore_case(std::string name,
+                                std::string &member,                  ///< The selected member of the set
+                                const std::set<std::string> &options, ///< The set of possibilities
+                                std::string description = "") {
+
+        std::string simple_name = CLI::detail::split(name, ',').at(0);
+        CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
+            member = detail::to_lower(res[0]);
+            auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
+                return detail::to_lower(val) == member;
+            });
+            if(iter == std::end(options))
+                throw ConversionError(member, simple_name);
+            else {
+                member = *iter;
+                return true;
+            }
+        };
+
+        Option *opt = add_option(name, fun, description, false);
+        opt->set_type_name_fn([&options]() {
+            return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
+        });
+
+        return opt;
+    }
+
+    /// Add set of options, string only, ignore case (default, R value)
     Option *add_set_ignore_case(std::string name,
-                                std::string &member,           ///< The selected member of the set
-                                std::set<std::string> options, ///< The set of posibilities
+                                std::string &member,                   ///< The selected member of the set
+                                const std::set<std::string> &&options, ///< The set of posibilities
                                 std::string description,
                                 bool defaulted) {
 
@@ -569,6 +698,37 @@ class App {
         return opt;
     }
 
+    /// Add set of options, string only, ignore case (default, L value)
+    Option *add_set_ignore_case(std::string name,
+                                std::string &member,                  ///< The selected member of the set
+                                const std::set<std::string> &options, ///< The set of posibilities
+                                std::string description,
+                                bool defaulted) {
+
+        std::string simple_name = CLI::detail::split(name, ',').at(0);
+        CLI::callback_t fun = [&member, &options, simple_name](CLI::results_t res) {
+            member = detail::to_lower(res[0]);
+            auto iter = std::find_if(std::begin(options), std::end(options), [&member](std::string val) {
+                return detail::to_lower(val) == member;
+            });
+            if(iter == std::end(options))
+                throw ConversionError(member, simple_name);
+            else {
+                member = *iter;
+                return true;
+            }
+        };
+
+        Option *opt = add_option(name, fun, description, defaulted);
+        opt->set_type_name_fn([&options]() {
+            return std::string(detail::type_name<std::string>()) + " in {" + detail::join(options) + "}";
+        });
+        if(defaulted) {
+            opt->set_default_str(member);
+        }
+        return opt;
+    }
+
     /// Add a complex number
     template <typename T>
     Option *add_complex(std::string name,
@@ -636,8 +796,7 @@ class App {
 
     /// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag
     App *add_subcommand(std::string name, std::string description = "") {
-        subcommands_.emplace_back(new App(description, this));
-        subcommands_.back()->name_ = name;
+        subcommands_.emplace_back(new App(description, name, this));
         for(const auto &subc : subcommands_)
             if(subc.get() != subcommands_.back().get())
                 if(subc->check_name(subcommands_.back()->name_) || subcommands_.back()->check_name(subc->name_))
@@ -722,8 +881,11 @@ class App {
 
     /// Parses the command line - throws errors
     /// This must be called after the options are in but before the rest of the program.
-    void parse(int argc, char **argv) {
-        name_ = argv[0];
+    void parse(int argc, const char *const *argv) {
+        // If the name is not set, read from command line
+        if(name_.empty())
+            name_ = argv[0];
+
         std::vector<std::string> args;
         for(int i = argc - 1; i > 0; i--)
             args.emplace_back(argv[i]);
@@ -755,6 +917,11 @@ class App {
             return e.get_exit_code();
         }
 
+        if(dynamic_cast<const CLI::CallForAllHelp *>(&e) != nullptr) {
+            out << help("", AppFormatMode::All);
+            return e.get_exit_code();
+        }
+
         if(e.get_exit_code() != static_cast<int>(ExitCodes::Success)) {
             if(failure_message_)
                 err << failure_message_(this, e) << std::flush;
@@ -792,18 +959,43 @@ class App {
         throw OptionNotFound(name);
     }
 
-    /// Get a subcommand pointer list to the currently selected subcommands (after parsing by default, in command line
-    /// order)
-    std::vector<App *> get_subcommands(bool parsed = true) const {
-        if(parsed) {
-            return parsed_subcommands_;
-        } else {
-            std::vector<App *> subcomms(subcommands_.size());
-            std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) {
-                return v.get();
-            });
-            return subcomms;
+    /// Get a subcommand pointer list to the currently selected subcommands (after parsing by by default, in command
+    /// line order; use parsed = false to get the original definition list.)
+    std::vector<App *> get_subcommands() const { return parsed_subcommands_; }
+
+    /// Get a filtered subcommand pointer list from the original definition list. An empty function will provide all
+    /// subcommands (const)
+    std::vector<const App *> get_subcommands(const std::function<bool(const App *)> &filter) const {
+        std::vector<const App *> subcomms(subcommands_.size());
+        std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) {
+            return v.get();
+        });
+
+        if(filter) {
+            subcomms.erase(std::remove_if(std::begin(subcomms),
+                                          std::end(subcomms),
+                                          [&filter](const App *app) { return !filter(app); }),
+                           std::end(subcomms));
+        }
+
+        return subcomms;
+    }
+
+    /// Get a filtered subcommand pointer list from the original definition list. An empty function will provide all
+    /// subcommands
+    std::vector<App *> get_subcommands(const std::function<bool(App *)> &filter) {
+        std::vector<App *> subcomms(subcommands_.size());
+        std::transform(std::begin(subcommands_), std::end(subcommands_), std::begin(subcomms), [](const App_p &v) {
+            return v.get();
+        });
+
+        if(filter) {
+            subcomms.erase(
+                std::remove_if(std::begin(subcomms), std::end(subcomms), [&filter](App *app) { return !filter(app); }),
+                std::end(subcomms));
         }
+
+        return subcomms;
     }
 
     /// Check to see if given subcommand was selected
@@ -827,156 +1019,76 @@ class App {
 
     /// Produce a string that could be read in as a config of the current values of the App. Set default_also to include
     /// default arguments. Prefix will add a string to the beginning of each option.
-    std::string
-    config_to_str(bool default_also = false, std::string prefix = "", bool write_description = false) const {
-        std::stringstream out;
-        for(const Option_p &opt : options_) {
-
-            // Only process option with a long-name and configurable
-            if(!opt->lnames_.empty() && opt->get_configurable()) {
-                std::string name = prefix + opt->lnames_[0];
-                std::string value;
-
-                // Non-flags
-                if(opt->get_type_size() != 0) {
-
-                    // If the option was found on command line
-                    if(opt->count() > 0)
-                        value = detail::inijoin(opt->results());
-
-                    // If the option has a default and is requested by optional argument
-                    else if(default_also && !opt->defaultval_.empty())
-                        value = opt->defaultval_;
-                    // Flag, one passed
-                } else if(opt->count() == 1) {
-                    value = "true";
-
-                    // Flag, multiple passed
-                } else if(opt->count() > 1) {
-                    value = std::to_string(opt->count());
-
-                    // Flag, not present
-                } else if(opt->count() == 0 && default_also) {
-                    value = "false";
-                }
-
-                if(!value.empty()) {
-                    if(write_description && opt->has_description()) {
-                        if(static_cast<int>(out.tellp()) != 0) {
-                            out << std::endl;
-                        }
-                        out << "; " << detail::fix_newlines("; ", opt->get_description()) << std::endl;
-                    }
-                    out << name << "=" << value << std::endl;
-                }
-            }
-        }
-        for(const App_p &subcom : subcommands_)
-            out << subcom->config_to_str(default_also, prefix + subcom->name_ + ".");
-        return out.str();
+    std::string config_to_str(bool default_also = false, bool write_description = false) const {
+        return config_formatter_->to_config(this, default_also, write_description, "");
     }
 
-    /// Makes a help message, with a column wid for column 1
-    std::string help(size_t wid = 30, std::string prev = "") const {
-        // Delegate to subcommand if needed
+    /// Makes a help message, using the currently configured formatter
+    /// Will only do one subcommand at a time
+    std::string help(std::string prev = "", AppFormatMode mode = AppFormatMode::Normal) const {
         if(prev.empty())
-            prev = name_;
+            prev = get_name();
         else
-            prev += " " + name_;
+            prev += " " + get_name();
 
+        // Delegate to subcommand if needed
         auto selected_subcommands = get_subcommands();
         if(!selected_subcommands.empty())
-            return selected_subcommands.at(0)->help(wid, prev);
-
-        std::stringstream out;
-        out << description_ << std::endl;
-        out << "Usage: " << prev;
+            return selected_subcommands.at(0)->help(prev);
+        else
+            return formatter_->make_help(this, prev, mode);
+    }
 
-        // Check for options_
-        bool npos = false;
-        std::set<std::string> groups;
-        for(const Option_p &opt : options_) {
-            if(opt->nonpositional()) {
-                npos = true;
-                groups.insert(opt->get_group());
-            }
-        }
+    ///@}
+    /// @name Getters
+    ///@{
 
-        if(npos)
-            out << " [OPTIONS]";
+    /// Access the formatter
+    std::shared_ptr<FormatterBase> get_formatter() const { return formatter_; }
 
-        // Positionals
-        bool pos = false;
-        for(const Option_p &opt : options_)
-            if(opt->get_positional()) {
-                // A hidden positional should still show up in the usage statement
-                // if(detail::to_lower(opt->get_group()).empty())
-                //    continue;
-                out << " " << opt->help_positional();
-                if(opt->_has_help_positional())
-                    pos = true;
-            }
+    /// Access the config formatter
+    std::shared_ptr<Config> get_config_formatter() const { return config_formatter_; }
 
-        if(!subcommands_.empty()) {
-            if(require_subcommand_min_ > 0)
-                out << " SUBCOMMAND";
-            else
-                out << " [SUBCOMMAND]";
-        }
+    /// Get the app or subcommand description
+    std::string get_description() const { return description_; }
 
-        out << std::endl;
+    /// Get the list of options (user facing function, so returns raw pointers), has optional filter function
+    std::vector<const Option *> get_options(const std::function<bool(const Option *)> filter = {}) const {
+        std::vector<const Option *> options(options_.size());
+        std::transform(std::begin(options_), std::end(options_), std::begin(options), [](const Option_p &val) {
+            return val.get();
+        });
 
-        // Positional descriptions
-        if(pos) {
-            out << std::endl << "Positionals:" << std::endl;
-            for(const Option_p &opt : options_) {
-                if(detail::to_lower(opt->get_group()).empty())
-                    continue; // Hidden
-                if(opt->_has_help_positional())
-                    detail::format_help(out, opt->help_pname(), opt->get_description(), wid);
-            }
+        if(filter) {
+            options.erase(std::remove_if(std::begin(options),
+                                         std::end(options),
+                                         [&filter](const Option *opt) { return !filter(opt); }),
+                          std::end(options));
         }
 
-        // Options
-        if(npos) {
-            for(const std::string &group : groups) {
-                if(detail::to_lower(group).empty())
-                    continue; // Hidden
-                out << std::endl << group << ":" << std::endl;
-                for(const Option_p &opt : options_) {
-                    if(opt->nonpositional() && opt->get_group() == group)
-                        detail::format_help(out, opt->help_name(true), opt->get_description(), wid);
-                }
-            }
-        }
+        return options;
+    }
 
-        // Subcommands
-        if(!subcommands_.empty()) {
-            std::set<std::string> subcmd_groups_seen;
-            for(const App_p &com : subcommands_) {
-                const std::string &group_key = detail::to_lower(com->get_group());
-                if(group_key.empty() || subcmd_groups_seen.count(group_key) != 0)
-                    continue; // Hidden or not in a group
-
-                subcmd_groups_seen.insert(group_key);
-                out << std::endl << com->get_group() << ":" << std::endl;
-                for(const App_p &new_com : subcommands_)
-                    if(detail::to_lower(new_com->get_group()) == group_key)
-                        detail::format_help(out, new_com->get_name(), new_com->description_, wid);
+    /// Get an option by name
+    const Option *get_option(std::string name) const {
+        for(const Option_p &opt : options_) {
+            if(opt->check_name(name)) {
+                return opt.get();
             }
         }
+        throw OptionNotFound(name);
+    }
 
-        if(!footer_.empty()) {
-            out << std::endl << footer_ << std::endl;
+    /// Get an option by name (non-const version)
+    Option *get_option(std::string name) {
+        for(Option_p &opt : options_) {
+            if(opt->check_name(name)) {
+                return opt.get();
+            }
         }
-
-        return out.str();
+        throw OptionNotFound(name);
     }
 
-    ///@}
-    /// @name Getters
-    ///@{
-
     /// Check the status of ignore_case
     bool get_ignore_case() const { return ignore_case_; }
 
@@ -1002,7 +1114,7 @@ class App {
     bool get_allow_extras() const { return allow_extras_; }
 
     /// Get the status of allow extras
-    bool get_allow_ini_extras() const { return allow_ini_extras_; }
+    bool get_allow_config_extras() const { return allow_config_extras_; }
 
     /// Get a pointer to the help flag.
     Option *get_help_ptr() { return help_ptr_; }
@@ -1010,14 +1122,21 @@ class App {
     /// Get a pointer to the help flag. (const)
     const Option *get_help_ptr() const { return help_ptr_; }
 
+    /// Get a pointer to the help all flag. (const)
+    const Option *get_help_all_ptr() const { return help_all_ptr_; }
+
     /// Get a pointer to the config option.
     Option *get_config_ptr() { return config_ptr_; }
 
+    /// Get a pointer to the config option. (const)
+    const Option *get_config_ptr() const { return config_ptr_; }
+
     /// Get the parent of this subcommand (or nullptr if master app)
     App *get_parent() { return parent_; }
 
-    /// Get a pointer to the config option. (const)
-    const Option *get_config_ptr() const { return config_ptr_; }
+    /// Get the parent of this subcommand (or nullptr if master app) (const version)
+    const App *get_parent() const { return parent_; }
+
     /// Get the name of the current app
     std::string get_name() const { return name_; }
 
@@ -1032,6 +1151,20 @@ class App {
         return local_name == name_to_check;
     }
 
+    /// Get the groups available directly from this option (in order)
+    std::vector<std::string> get_groups() const {
+        std::vector<std::string> groups;
+
+        for(const Option_p &opt : options_) {
+            // Add group if it is not already in there
+            if(std::find(groups.begin(), groups.end(), opt->get_group()) == groups.end()) {
+                groups.push_back(opt->get_group());
+            }
+        }
+
+        return groups;
+    }
+
     /// This gets a vector of pointers with the original parse order
     const std::vector<Option *> &parse_order() const { return parse_order_; }
 
@@ -1131,9 +1264,9 @@ class App {
             _parse_single(args, positional_only);
         }
 
-        if(help_ptr_ != nullptr && help_ptr_->count() > 0) {
-            throw CallForHelp();
-        }
+        for(const Option_p &opt : options_)
+            if(opt->get_short_circuit() && opt->count() > 0)
+                opt->run_callback();
 
         // Process an INI file
         if(config_ptr_ != nullptr) {
@@ -1143,12 +1276,8 @@ class App {
             }
             if(!config_name_.empty()) {
                 try {
-                    std::vector<detail::ini_ret_t> values = detail::parse_ini(config_name_);
-                    while(!values.empty()) {
-                        if(!_parse_ini(values)) {
-                            throw INIError::Extras(values.back().fullname);
-                        }
-                    }
+                    std::vector<ConfigItem> values = config_formatter_->from_file(config_name_);
+                    _parse_config(values);
                 } catch(const FileError &) {
                     if(config_required_)
                         throw;
@@ -1195,20 +1324,20 @@ class App {
             if(opt->get_required() || opt->count() != 0) {
                 // Make sure enough -N arguments parsed (+N is already handled in parsing function)
                 if(opt->get_items_expected() < 0 && opt->count() < static_cast<size_t>(-opt->get_items_expected()))
-                    throw ArgumentMismatch::AtLeast(opt->single_name(), -opt->get_items_expected());
+                    throw ArgumentMismatch::AtLeast(opt->get_name(), -opt->get_items_expected());
 
                 // Required but empty
                 if(opt->get_required() && opt->count() == 0)
-                    throw RequiredError(opt->single_name());
+                    throw RequiredError(opt->get_name());
             }
             // Requires
             for(const Option *opt_req : opt->requires_)
                 if(opt->count() > 0 && opt_req->count() == 0)
-                    throw RequiresError(opt->single_name(), opt_req->single_name());
+                    throw RequiresError(opt->get_name(), opt_req->get_name());
             // Excludes
             for(const Option *opt_ex : opt->excludes_)
                 if(opt->count() > 0 && opt_ex->count() != 0)
-                    throw ExcludesError(opt->single_name(), opt_ex->single_name());
+                    throw ExcludesError(opt->get_name(), opt_ex->get_name());
         }
 
         auto selected_subcommands = get_subcommands();
@@ -1226,70 +1355,54 @@ class App {
         }
     }
 
-    /// Parse one ini param, return false if not found in any subcommand, remove if it is
+    /// Parse one config param, return false if not found in any subcommand, remove if it is
     ///
     /// If this has more than one dot.separated.name, go into the subcommand matching it
     /// Returns true if it managed to find the option, if false you'll need to remove the arg manually.
-    bool _parse_ini(std::vector<detail::ini_ret_t> &args) {
-        detail::ini_ret_t &current = args.back();
-        std::string parent = current.parent(); // respects current.level
-        std::string name = current.name();
-
-        // If a parent is listed, go to a subcommand
-        if(!parent.empty()) {
-            current.level++;
-            for(const App_p &com : subcommands_)
-                if(com->check_name(parent))
-                    return com->_parse_ini(args);
-            return false;
+    void _parse_config(std::vector<ConfigItem> &args) {
+        for(ConfigItem item : args) {
+            if(!_parse_single_config(item) && !allow_config_extras_)
+                throw ConfigError::Extras(item.fullname());
         }
+    }
 
-        auto op_ptr = std::find_if(
-            std::begin(options_), std::end(options_), [name](const Option_p &v) { return v->check_lname(name); });
+    /// Fill in a single config option
+    bool _parse_single_config(const ConfigItem &item, size_t level = 0) {
+        if(level < item.parents.size()) {
+            App *subcom;
+            try {
+                std::cout << item.parents.at(level) << std::endl;
+                subcom = get_subcommand(item.parents.at(level));
+            } catch(const OptionNotFound &) {
+                return false;
+            }
+            return subcom->_parse_single_config(item, level + 1);
+        }
 
-        if(op_ptr == std::end(options_)) {
-            if(allow_ini_extras_) {
+        Option *op;
+        try {
+            op = get_option("--" + item.name);
+        } catch(const OptionNotFound &) {
+            // If the option was not present
+            if(get_allow_config_extras())
                 // Should we worry about classifying the extras properly?
-                missing_.emplace_back(detail::Classifer::NONE, current.fullname);
-                args.pop_back();
-                return true;
-            }
+                missing_.emplace_back(detail::Classifer::NONE, item.fullname());
             return false;
         }
 
-        // Let's not go crazy with pointer syntax
-        Option_p &op = *op_ptr;
-
         if(!op->get_configurable())
-            throw INIError::NotConfigurable(current.fullname);
+            throw ConfigError::NotConfigurable(item.fullname());
 
-        if(op->results_.empty()) {
+        if(op->empty()) {
             // Flag parsing
             if(op->get_type_size() == 0) {
-                if(current.inputs.size() == 1) {
-                    std::string val = current.inputs.at(0);
-                    val = detail::to_lower(val);
-                    if(val == "true" || val == "on" || val == "yes")
-                        op->results_ = {""};
-                    else if(val == "false" || val == "off" || val == "no")
-                        ;
-                    else
-                        try {
-                            size_t ui = std::stoul(val);
-                            for(size_t i = 0; i < ui; i++)
-                                op->results_.emplace_back("");
-                        } catch(const std::invalid_argument &) {
-                            throw ConversionError::TrueFalse(current.fullname);
-                        }
-                } else
-                    throw ConversionError::TooManyInputsFlag(current.fullname);
+                op->set_results(config_formatter_->to_flag(item));
             } else {
-                op->results_ = current.inputs;
+                op->set_results(item.inputs);
                 op->run_callback();
             }
         }
 
-        args.pop_back();
         return true;
     }
 
@@ -1482,7 +1595,7 @@ class App {
             }
 
             if(num > 0) {
-                throw ArgumentMismatch::TypedAtLeast(op->single_name(), num, op->get_type_name());
+                throw ArgumentMismatch::TypedAtLeast(op->get_name(), num, op->get_type_name());
             }
         }
 
@@ -1498,7 +1611,7 @@ namespace FailureMessage {
 inline std::string simple(const App *app, const Error &e) {
     std::string header = std::string(e.what()) + "\n";
     if(app->get_help_ptr() != nullptr)
-        header += "Run with " + app->get_help_ptr()->single_name() + " for more information.\n";
+        header += "Run with " + app->get_help_ptr()->get_name() + " for more information.\n";
     return header;
 }
 
diff --git a/packages/CLI11/include/CLI/CLI.hpp b/packages/CLI11/include/CLI/CLI.hpp
index 662e833dd47d32f029bd564684fb689250383d24..2feb19247843b99293198f0b881d014fee066612 100644
--- a/packages/CLI11/include/CLI/CLI.hpp
+++ b/packages/CLI11/include/CLI/CLI.hpp
@@ -20,10 +20,16 @@
 
 #include "CLI/Split.hpp"
 
-#include "CLI/Ini.hpp"
+#include "CLI/ConfigFwd.hpp"
 
 #include "CLI/Validators.hpp"
 
+#include "CLI/FormatterFwd.hpp"
+
 #include "CLI/Option.hpp"
 
 #include "CLI/App.hpp"
+
+#include "CLI/Config.hpp"
+
+#include "CLI/Formatter.hpp"
diff --git a/packages/CLI11/include/CLI/Config.hpp b/packages/CLI11/include/CLI/Config.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..33f8619739745acba19dcdee5200621dc4a0e863
--- /dev/null
+++ b/packages/CLI11/include/CLI/Config.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+// Distributed under the 3-Clause BSD License.  See accompanying
+// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "CLI/App.hpp"
+#include "CLI/ConfigFwd.hpp"
+#include "CLI/StringTools.hpp"
+
+namespace CLI {
+
+inline std::string
+ConfigINI::to_config(const App *app, bool default_also, bool write_description, std::string prefix) const {
+    std::stringstream out;
+    for(const Option *opt : app->get_options({})) {
+
+        // Only process option with a long-name and configurable
+        if(!opt->get_lnames().empty() && opt->get_configurable()) {
+            std::string name = prefix + opt->get_lnames()[0];
+            std::string value;
+
+            // Non-flags
+            if(opt->get_type_size() != 0) {
+
+                // If the option was found on command line
+                if(opt->count() > 0)
+                    value = detail::inijoin(opt->results());
+
+                // If the option has a default and is requested by optional argument
+                else if(default_also && !opt->get_defaultval().empty())
+                    value = opt->get_defaultval();
+                // Flag, one passed
+            } else if(opt->count() == 1) {
+                value = "true";
+
+                // Flag, multiple passed
+            } else if(opt->count() > 1) {
+                value = std::to_string(opt->count());
+
+                // Flag, not present
+            } else if(opt->count() == 0 && default_also) {
+                value = "false";
+            }
+
+            if(!value.empty()) {
+                if(write_description && opt->has_description()) {
+                    if(static_cast<int>(out.tellp()) != 0) {
+                        out << std::endl;
+                    }
+                    out << "; " << detail::fix_newlines("; ", opt->get_description()) << std::endl;
+                }
+                out << name << "=" << value << std::endl;
+            }
+        }
+    }
+
+    for(const App *subcom : app->get_subcommands({}))
+        out << to_config(subcom, default_also, write_description, prefix + subcom->get_name() + ".");
+
+    return out.str();
+}
+
+} // namespace CLI
diff --git a/packages/CLI11/include/CLI/ConfigFwd.hpp b/packages/CLI11/include/CLI/ConfigFwd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ee2e1cbe6a402944f8c0aa8eebb93353572ff03
--- /dev/null
+++ b/packages/CLI11/include/CLI/ConfigFwd.hpp
@@ -0,0 +1,159 @@
+#pragma once
+
+// Distributed under the 3-Clause BSD License.  See accompanying
+// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "CLI/StringTools.hpp"
+
+namespace CLI {
+
+class App;
+
+namespace detail {
+
+inline std::string inijoin(std::vector<std::string> args) {
+    std::ostringstream s;
+    size_t start = 0;
+    for(const auto &arg : args) {
+        if(start++ > 0)
+            s << " ";
+
+        auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); });
+        if(it == arg.end())
+            s << arg;
+        else if(arg.find(R"(")") == std::string::npos)
+            s << R"(")" << arg << R"(")";
+        else
+            s << R"(')" << arg << R"(')";
+    }
+
+    return s.str();
+}
+
+} // namespace detail
+
+struct ConfigItem {
+    /// This is the list of parents
+    std::vector<std::string> parents;
+
+    /// This is the name
+    std::string name;
+
+    /// Listing of inputs
+    std::vector<std::string> inputs;
+
+    /// The list of parents and name joined by "."
+    std::string fullname() const {
+        std::vector<std::string> tmp = parents;
+        tmp.emplace_back(name);
+        return detail::join(tmp, ".");
+    }
+};
+
+/// This class provides a converter for configuration files.
+class Config {
+  protected:
+    std::vector<ConfigItem> items;
+
+  public:
+    /// Convert an app into a configuration
+    virtual std::string to_config(const App *, bool, bool, std::string) const = 0;
+
+    /// Convert a configuration into an app
+    virtual std::vector<ConfigItem> from_config(std::istream &) const = 0;
+
+    /// Convert a flag to a bool
+    virtual std::vector<std::string> to_flag(const ConfigItem &) const = 0;
+
+    /// Parse a config file, throw an error (ParseError:ConfigParseError or FileError) on failure
+    std::vector<ConfigItem> from_file(const std::string &name) {
+        std::ifstream input{name};
+        if(!input.good())
+            throw FileError::Missing(name);
+
+        return from_config(input);
+    }
+
+    /// virtual destructor
+    virtual ~Config() = default;
+};
+
+/// This converter works with INI files
+class ConfigINI : public Config {
+  public:
+    std::string to_config(const App *, bool default_also, bool write_description, std::string prefix) const override;
+
+    std::vector<std::string> to_flag(const ConfigItem &item) const override {
+        if(item.inputs.size() == 1) {
+            std::string val = item.inputs.at(0);
+            val = detail::to_lower(val);
+
+            if(val == "true" || val == "on" || val == "yes") {
+                return std::vector<std::string>(1);
+            } else if(val == "false" || val == "off" || val == "no") {
+                return std::vector<std::string>();
+            } else {
+                try {
+                    size_t ui = std::stoul(val);
+                    return std::vector<std::string>(ui);
+                } catch(const std::invalid_argument &) {
+                    throw ConversionError::TrueFalse(item.fullname());
+                }
+            }
+        } else {
+            throw ConversionError::TooManyInputsFlag(item.fullname());
+        }
+    }
+
+    std::vector<ConfigItem> from_config(std::istream &input) const override {
+        std::string line;
+        std::string section = "default";
+
+        std::vector<ConfigItem> output;
+
+        while(getline(input, line)) {
+            std::vector<std::string> items;
+
+            detail::trim(line);
+            size_t len = line.length();
+            if(len > 1 && line[0] == '[' && line[len - 1] == ']') {
+                section = line.substr(1, len - 2);
+            } else if(len > 0 && line[0] != ';') {
+                output.emplace_back();
+                ConfigItem &out = output.back();
+
+                // Find = in string, split and recombine
+                auto pos = line.find('=');
+                if(pos != std::string::npos) {
+                    out.name = detail::trim_copy(line.substr(0, pos));
+                    std::string item = detail::trim_copy(line.substr(pos + 1));
+                    items = detail::split_up(item);
+                } else {
+                    out.name = detail::trim_copy(line);
+                    items = {"ON"};
+                }
+
+                if(detail::to_lower(section) != "default") {
+                    out.parents = {section};
+                }
+
+                if(out.name.find('.') != std::string::npos) {
+                    std::vector<std::string> plist = detail::split(out.name, '.');
+                    out.name = plist.back();
+                    plist.pop_back();
+                    out.parents.insert(out.parents.end(), plist.begin(), plist.end());
+                }
+
+                out.inputs.insert(std::end(out.inputs), std::begin(items), std::end(items));
+            }
+        }
+        return output;
+    }
+};
+
+} // namespace CLI
diff --git a/packages/CLI11/include/CLI/Error.hpp b/packages/CLI11/include/CLI/Error.hpp
index 09104a651f5ef17ffeb5e2f05f6dc0924aaacfa1..6a50613586182e5d51b14bdd8c2a3345d6fc2fcf 100644
--- a/packages/CLI11/include/CLI/Error.hpp
+++ b/packages/CLI11/include/CLI/Error.hpp
@@ -13,7 +13,8 @@
 
 namespace CLI {
 
-// Use one of these on all error classes
+// Use one of these on all error classes.
+// These are temporary and are undef'd at the end of this file.
 #define CLI11_ERROR_DEF(parent, name)                                                                                  \
   protected:                                                                                                           \
     name(std::string name, std::string msg, int exit_code) : parent(std::move(name), std::move(msg), exit_code) {}     \
@@ -26,7 +27,7 @@ namespace CLI {
 
 // This is added after the one above if a class is used directly and builds its own message
 #define CLI11_ERROR_SIMPLE(name)                                                                                       \
-    name(std::string msg) : name(#name, msg, ExitCodes::name) {}
+    explicit name(std::string msg) : name(#name, msg, ExitCodes::name) {}
 
 /// These codes are part of every error in CLI. They can be obtained from e using e.exit_code or as a quick shortcut,
 /// int values from e.get_error_code().
@@ -42,7 +43,7 @@ enum class ExitCodes {
     RequiresError,
     ExcludesError,
     ExtrasError,
-    INIError,
+    ConfigError,
     InvalidError,
     HorribleError,
     OptionNotFound,
@@ -126,7 +127,7 @@ class BadNameString : public ConstructionError {
 /// Thrown when an option already exists
 class OptionAlreadyAdded : public ConstructionError {
     CLI11_ERROR_DEF(ConstructionError, OptionAlreadyAdded)
-    OptionAlreadyAdded(std::string name)
+    explicit OptionAlreadyAdded(std::string name)
         : OptionAlreadyAdded(name + " is already added", ExitCodes::OptionAlreadyAdded) {}
     static OptionAlreadyAdded Requires(std::string name, std::string other) {
         return OptionAlreadyAdded(name + " requires " + other, ExitCodes::OptionAlreadyAdded);
@@ -157,10 +158,17 @@ class CallForHelp : public ParseError {
     CallForHelp() : CallForHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
 };
 
+/// Usually somethign like --help-all on command line
+class CallForAllHelp : public ParseError {
+    CLI11_ERROR_DEF(ParseError, CallForAllHelp)
+    CallForAllHelp()
+        : CallForAllHelp("This should be caught in your main function, see examples", ExitCodes::Success) {}
+};
+
 /// Does not output a diagnostic in CLI11_PARSE, but allows to return from main() with a specific error code.
 class RuntimeError : public ParseError {
     CLI11_ERROR_DEF(ParseError, RuntimeError)
-    RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {}
+    explicit RuntimeError(int exit_code = 1) : RuntimeError("Runtime error", exit_code) {}
 };
 
 /// Thrown when parsing an INI file and it is missing
@@ -190,13 +198,13 @@ class ConversionError : public ParseError {
 class ValidationError : public ParseError {
     CLI11_ERROR_DEF(ParseError, ValidationError)
     CLI11_ERROR_SIMPLE(ValidationError)
-    ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {}
+    explicit ValidationError(std::string name, std::string msg) : ValidationError(name + ": " + msg) {}
 };
 
 /// Thrown when a required option is missing
 class RequiredError : public ParseError {
     CLI11_ERROR_DEF(ParseError, RequiredError)
-    RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
+    explicit RequiredError(std::string name) : RequiredError(name + " is required", ExitCodes::RequiredError) {}
     static RequiredError Subcommand(size_t min_subcom) {
         if(min_subcom == 1)
             return RequiredError("A subcommand");
@@ -242,7 +250,7 @@ class ExcludesError : public ParseError {
 /// Thrown when too many positionals or options are found
 class ExtrasError : public ParseError {
     CLI11_ERROR_DEF(ParseError, ExtrasError)
-    ExtrasError(std::vector<std::string> args)
+    explicit ExtrasError(std::vector<std::string> args)
         : ExtrasError((args.size() > 1 ? "The following arguments were not expected: "
                                        : "The following argument was not expected: ") +
                           detail::rjoin(args, " "),
@@ -250,19 +258,19 @@ class ExtrasError : public ParseError {
 };
 
 /// Thrown when extra values are found in an INI file
-class INIError : public ParseError {
-    CLI11_ERROR_DEF(ParseError, INIError)
-    CLI11_ERROR_SIMPLE(INIError)
-    static INIError Extras(std::string item) { return INIError("INI was not able to parse " + item); }
-    static INIError NotConfigurable(std::string item) {
-        return INIError(item + ": This option is not allowed in a configuration file");
+class ConfigError : public ParseError {
+    CLI11_ERROR_DEF(ParseError, ConfigError)
+    CLI11_ERROR_SIMPLE(ConfigError)
+    static ConfigError Extras(std::string item) { return ConfigError("INI was not able to parse " + item); }
+    static ConfigError NotConfigurable(std::string item) {
+        return ConfigError(item + ": This option is not allowed in a configuration file");
     }
 };
 
 /// Thrown when validation fails before parsing
 class InvalidError : public ParseError {
     CLI11_ERROR_DEF(ParseError, InvalidError)
-    InvalidError(std::string name)
+    explicit InvalidError(std::string name)
         : InvalidError(name + ": Too many positional arguments with unlimited expected args", ExitCodes::InvalidError) {
     }
 };
@@ -279,9 +287,12 @@ class HorribleError : public ParseError {
 /// Thrown when counting a non-existent option
 class OptionNotFound : public Error {
     CLI11_ERROR_DEF(Error, OptionNotFound)
-    OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
+    explicit OptionNotFound(std::string name) : OptionNotFound(name + " not found", ExitCodes::OptionNotFound) {}
 };
 
+#undef CLI11_ERROR_DEF
+#undef CLI11_ERROR_SIMPLE
+
 /// @}
 
 } // namespace CLI
diff --git a/packages/CLI11/include/CLI/Formatter.hpp b/packages/CLI11/include/CLI/Formatter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..82e641a3b0bb0464e9faca925f8a9153c37f9985
--- /dev/null
+++ b/packages/CLI11/include/CLI/Formatter.hpp
@@ -0,0 +1,238 @@
+#pragma once
+
+// Distributed under the 3-Clause BSD License.  See accompanying
+// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+
+#include <string>
+
+#include "CLI/App.hpp"
+#include "CLI/Formatter.hpp"
+
+namespace CLI {
+
+inline std::string
+Formatter::make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const {
+    std::stringstream out;
+
+    out << "\n" << group << ":\n";
+    for(const Option *opt : opts) {
+        out << make_option(opt, is_positional);
+    }
+
+    return out.str();
+}
+
+inline std::string Formatter::make_positionals(const App *app) const {
+    std::vector<const Option *> opts =
+        app->get_options([](const Option *opt) { return !opt->get_group().empty() && opt->get_positional(); });
+
+    if(opts.empty())
+        return std::string();
+    else
+        return make_group(get_label("Positionals"), true, opts);
+}
+
+inline std::string Formatter::make_groups(const App *app, AppFormatMode mode) const {
+    std::stringstream out;
+    std::vector<std::string> groups = app->get_groups();
+
+    // Options
+    for(const std::string &group : groups) {
+        std::vector<const Option *> opts = app->get_options([app, mode, &group](const Option *opt) {
+            return opt->get_group() == group                    // Must be in the right group
+                   && opt->nonpositional()                      // Must not be a positional
+                   && (mode != AppFormatMode::Sub               // If mode is Sub, then
+                       || (app->get_help_ptr() != opt           // Ignore help pointer
+                           && app->get_help_all_ptr() != opt)); // Ignore help all pointer
+        });
+        if(!group.empty() && !opts.empty()) {
+            out << make_group(group, false, opts);
+
+            if(group != groups.back())
+                out << "\n";
+        }
+    }
+
+    return out.str();
+}
+
+inline std::string Formatter::make_description(const App *app) const {
+    std::string desc = app->get_description();
+
+    if(!desc.empty())
+        return desc + "\n";
+    else
+        return "";
+}
+
+inline std::string Formatter::make_usage(const App *app, std::string name) const {
+    std::stringstream out;
+
+    out << get_label("Usage") << ":" << (name.empty() ? "" : " ") << name;
+
+    std::vector<std::string> groups = app->get_groups();
+
+    // Print an Options badge if any options exist
+    std::vector<const Option *> non_pos_options =
+        app->get_options([](const Option *opt) { return opt->nonpositional(); });
+    if(!non_pos_options.empty())
+        out << " [" << get_label("OPTIONS") << "]";
+
+    // Positionals need to be listed here
+    std::vector<const Option *> positionals = app->get_options([](const Option *opt) { return opt->get_positional(); });
+
+    // Print out positionals if any are left
+    if(!positionals.empty()) {
+        // Convert to help names
+        std::vector<std::string> positional_names(positionals.size());
+        std::transform(positionals.begin(), positionals.end(), positional_names.begin(), [this](const Option *opt) {
+            return make_option_usage(opt);
+        });
+
+        out << " " << detail::join(positional_names, " ");
+    }
+
+    // Add a marker if subcommands are expected or optional
+    if(!app->get_subcommands({}).empty()) {
+        out << " " << (app->get_require_subcommand_min() == 0 ? "[" : "")
+            << get_label(app->get_require_subcommand_max() < 2 || app->get_require_subcommand_min() > 1 ? "SUBCOMMAND"
+                                                                                                        : "SUBCOMMANDS")
+            << (app->get_require_subcommand_min() == 0 ? "]" : "");
+    }
+
+    out << std::endl;
+
+    return out.str();
+}
+
+inline std::string Formatter::make_footer(const App *app) const {
+    std::string footer = app->get_footer();
+    if(!footer.empty())
+        return footer + "\n";
+    else
+        return "";
+}
+
+inline std::string Formatter::make_help(const App *app, std::string name, AppFormatMode mode) const {
+
+    // This immediatly forwards to the make_expanded method. This is done this way so that subcommands can
+    // have overridden formatters
+    if(mode == AppFormatMode::Sub)
+        return make_expanded(app);
+
+    std::stringstream out;
+
+    out << make_description(app);
+    out << make_usage(app, name);
+    out << make_positionals(app);
+    out << make_groups(app, mode);
+    out << make_subcommands(app, mode);
+    out << make_footer(app);
+
+    return out.str();
+}
+
+inline std::string Formatter::make_subcommands(const App *app, AppFormatMode mode) const {
+    std::stringstream out;
+
+    std::vector<const App *> subcommands = app->get_subcommands({});
+
+    // Make a list in definition order of the groups seen
+    std::vector<std::string> subcmd_groups_seen;
+    for(const App *com : subcommands) {
+        std::string group_key = com->get_group();
+        if(!group_key.empty() &&
+           std::find_if(subcmd_groups_seen.begin(), subcmd_groups_seen.end(), [&group_key](std::string a) {
+               return detail::to_lower(a) == detail::to_lower(group_key);
+           }) == subcmd_groups_seen.end())
+            subcmd_groups_seen.push_back(group_key);
+    }
+
+    // For each group, filter out and print subcommands
+    for(const std::string &group : subcmd_groups_seen) {
+        out << "\n" << group << ":\n";
+        std::vector<const App *> subcommands_group = app->get_subcommands(
+            [&group](const App *app) { return detail::to_lower(app->get_group()) == detail::to_lower(group); });
+        for(const App *new_com : subcommands_group) {
+            if(mode != AppFormatMode::All) {
+                out << make_subcommand(new_com);
+            } else {
+                out << new_com->help(new_com->get_name(), AppFormatMode::Sub);
+                if(new_com != subcommands_group.back())
+                    out << "\n";
+            }
+        }
+    }
+
+    return out.str();
+}
+
+inline std::string Formatter::make_subcommand(const App *sub) const {
+    std::stringstream out;
+    detail::format_help(out, sub->get_name(), sub->get_description(), column_width_);
+    return out.str();
+}
+
+inline std::string Formatter::make_expanded(const App *sub) const {
+    std::stringstream out;
+    if(sub->get_description().empty())
+        out << sub->get_name();
+    else
+        out << sub->get_name() << " -> " << sub->get_description();
+    out << make_groups(sub, AppFormatMode::Sub);
+    return out.str();
+}
+
+inline std::string Formatter::make_option_name(const Option *opt, bool is_positional) const {
+    if(is_positional)
+        return opt->get_name(true, false);
+    else
+        return opt->get_name(false, true);
+}
+
+inline std::string Formatter::make_option_opts(const Option *opt) const {
+    std::stringstream out;
+
+    if(opt->get_type_size() != 0) {
+        if(!opt->get_type_name().empty())
+            out << " " << get_label(opt->get_type_name());
+        if(!opt->get_defaultval().empty())
+            out << "=" << opt->get_defaultval();
+        if(opt->get_expected() > 1)
+            out << " x " << opt->get_expected();
+        if(opt->get_expected() == -1)
+            out << " ...";
+        if(opt->get_required())
+            out << " " << get_label("REQUIRED");
+    }
+    if(!opt->get_envname().empty())
+        out << " (" << get_label("Env") << ":" << opt->get_envname() << ")";
+    if(!opt->get_needs().empty()) {
+        out << " " << get_label("Needs") << ":";
+        for(const Option *op : opt->get_needs())
+            out << " " << op->get_name();
+    }
+    if(!opt->get_excludes().empty()) {
+        out << " " << get_label("Excludes") << ":";
+        for(const Option *op : opt->get_excludes())
+            out << " " << op->get_name();
+    }
+    return out.str();
+}
+
+inline std::string Formatter::make_option_desc(const Option *opt) const { return opt->get_description(); }
+
+inline std::string Formatter::make_option_usage(const Option *opt) const {
+    // Note that these are positionals usages
+    std::stringstream out;
+    out << make_option_name(opt, true);
+
+    if(opt->get_expected() > 1)
+        out << "(" << std::to_string(opt->get_expected()) << "x)";
+    else if(opt->get_expected() < 0)
+        out << "...";
+
+    return opt->get_required() ? out.str() : "[" + out.str() + "]";
+}
+
+} // namespace CLI
diff --git a/packages/CLI11/include/CLI/FormatterFwd.hpp b/packages/CLI11/include/CLI/FormatterFwd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..92bfe65112f0d6331ffc59326ea24a119f6e6eca
--- /dev/null
+++ b/packages/CLI11/include/CLI/FormatterFwd.hpp
@@ -0,0 +1,167 @@
+#pragma once
+
+// Distributed under the 3-Clause BSD License.  See accompanying
+// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
+
+#include <map>
+#include <string>
+#include <utility>
+
+#include "CLI/StringTools.hpp"
+
+namespace CLI {
+
+class Option;
+class App;
+
+/// This enum signifies the type of help requested
+///
+/// This is passed in by App; all user classes must accept this as
+/// the second argument.
+
+enum class AppFormatMode {
+    Normal, //< The normal, detailed help
+    All,    //< A fully expanded help
+    Sub,    //< Used when printed as part of expanded subcommand
+};
+
+/// This is the minimum requirements to run a formatter.
+///
+/// A user can subclass this is if they do not care at all
+/// about the structure in CLI::Formatter.
+class FormatterBase {
+  protected:
+    /// @name Options
+    ///@{
+
+    /// The width of the first column
+    size_t column_width_{30};
+
+    /// @brief The required help printout labels (user changeable)
+    /// Values are Needs, Excludes, etc.
+    std::map<std::string, std::string> labels_;
+
+    ///@}
+    /// @name Basic
+    ///@{
+
+  public:
+    FormatterBase() = default;
+    FormatterBase(const FormatterBase &) = default;
+    FormatterBase(FormatterBase &&) = default;
+    virtual ~FormatterBase() = default;
+
+    /// This is the key method that puts together help
+    virtual std::string make_help(const App *, std::string, AppFormatMode) const = 0;
+
+    ///@}
+    /// @name Setters
+    ///@{
+
+    /// Set the "REQUIRED" label
+    void label(std::string key, std::string val) { labels_[key] = val; }
+
+    /// Set the column width
+    void column_width(size_t val) { column_width_ = val; }
+
+    ///@}
+    /// @name Getters
+    ///@{
+
+    /// Get the current value of a name (REQUIRED, etc.)
+    std::string get_label(std::string key) const {
+        if(labels_.find(key) == labels_.end())
+            return key;
+        else
+            return labels_.at(key);
+    }
+
+    /// Get the current column width
+    size_t get_column_width() const { return column_width_; }
+
+    ///@}
+};
+
+/// This is a specialty override for lambda functions
+class FormatterLambda final : public FormatterBase {
+    using funct_t = std::function<std::string(const App *, std::string, AppFormatMode)>;
+
+    funct_t lambda_;
+
+  public:
+    explicit FormatterLambda(funct_t funct) : lambda_(std::move(funct)) {}
+
+    /// This will simply call the lambda function
+    std::string make_help(const App *app, std::string name, AppFormatMode mode) const override {
+        return lambda_(app, name, mode);
+    }
+};
+
+class Formatter : public FormatterBase {
+  public:
+    Formatter() = default;
+    Formatter(const Formatter &) = default;
+    Formatter(Formatter &&) = default;
+
+    /// @name Overridables
+    ///@{
+
+    /// This prints out a group of options with title
+    ///
+    virtual std::string make_group(std::string group, bool is_positional, std::vector<const Option *> opts) const;
+
+    /// This prints out just the positionals "group"
+    virtual std::string make_positionals(const App *app) const;
+
+    /// This prints out all the groups of options
+    std::string make_groups(const App *app, AppFormatMode mode) const;
+
+    /// This prints out all the subcommands
+    virtual std::string make_subcommands(const App *app, AppFormatMode mode) const;
+
+    /// This prints out a subcommand
+    virtual std::string make_subcommand(const App *sub) const;
+
+    /// This prints out a subcommand in help-all
+    virtual std::string make_expanded(const App *sub) const;
+
+    /// This prints out all the groups of options
+    virtual std::string make_footer(const App *app) const;
+
+    /// This displays the description line
+    virtual std::string make_description(const App *app) const;
+
+    /// This displays the usage line
+    virtual std::string make_usage(const App *app, std::string name) const;
+
+    /// This puts everything together
+    std::string make_help(const App *, std::string, AppFormatMode) const override;
+
+    ///@}
+    /// @name Options
+    ///@{
+
+    /// This prints out an option help line, either positional or optional form
+    virtual std::string make_option(const Option *opt, bool is_positional) const {
+        std::stringstream out;
+        detail::format_help(
+            out, make_option_name(opt, is_positional) + make_option_opts(opt), make_option_desc(opt), column_width_);
+        return out.str();
+    }
+
+    /// @brief This is the name part of an option, Default: left column
+    virtual std::string make_option_name(const Option *, bool) const;
+
+    /// @brief This is the options part of the name, Default: combined into left column
+    virtual std::string make_option_opts(const Option *) const;
+
+    /// @brief This is the description. Default: Right column, on new line if left column too large
+    virtual std::string make_option_desc(const Option *) const;
+
+    /// @brief This is used to print the name on the USAGE line
+    virtual std::string make_option_usage(const Option *opt) const;
+
+    ///@}
+};
+
+} // namespace CLI
diff --git a/packages/CLI11/include/CLI/Ini.hpp b/packages/CLI11/include/CLI/Ini.hpp
deleted file mode 100644
index f9999fff39401f6c73fa6f415cc23ebebb377106..0000000000000000000000000000000000000000
--- a/packages/CLI11/include/CLI/Ini.hpp
+++ /dev/null
@@ -1,115 +0,0 @@
-#pragma once
-
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
-
-#include <algorithm>
-#include <fstream>
-#include <iostream>
-#include <string>
-
-#include "CLI/StringTools.hpp"
-
-namespace CLI {
-namespace detail {
-
-inline std::string inijoin(std::vector<std::string> args) {
-    std::ostringstream s;
-    size_t start = 0;
-    for(const auto &arg : args) {
-        if(start++ > 0)
-            s << " ";
-
-        auto it = std::find_if(arg.begin(), arg.end(), [](char ch) { return std::isspace<char>(ch, std::locale()); });
-        if(it == arg.end())
-            s << arg;
-        else if(arg.find(R"(")") == std::string::npos)
-            s << R"(")" << arg << R"(")";
-        else
-            s << R"(')" << arg << R"(')";
-    }
-
-    return s.str();
-}
-
-struct ini_ret_t {
-    /// This is the full name with dots
-    std::string fullname;
-
-    /// Listing of inputs
-    std::vector<std::string> inputs;
-
-    /// Current parent level
-    size_t level = 0;
-
-    /// Return parent or empty string, based on level
-    ///
-    /// Level 0, a.b.c would return a
-    /// Level 1, a.b.c could return b
-    std::string parent() const {
-        std::vector<std::string> plist = detail::split(fullname, '.');
-        if(plist.size() > (level + 1))
-            return plist[level];
-        else
-            return "";
-    }
-
-    /// Return name
-    std::string name() const {
-        std::vector<std::string> plist = detail::split(fullname, '.');
-        return plist.at(plist.size() - 1);
-    }
-};
-
-/// Internal parsing function
-inline std::vector<ini_ret_t> parse_ini(std::istream &input) {
-    std::string name, line;
-    std::string section = "default";
-
-    std::vector<ini_ret_t> output;
-
-    while(getline(input, line)) {
-        std::vector<std::string> items;
-
-        detail::trim(line);
-        size_t len = line.length();
-        if(len > 1 && line[0] == '[' && line[len - 1] == ']') {
-            section = line.substr(1, len - 2);
-        } else if(len > 0 && line[0] != ';') {
-            output.emplace_back();
-            ini_ret_t &out = output.back();
-
-            // Find = in string, split and recombine
-            auto pos = line.find("=");
-            if(pos != std::string::npos) {
-                name = detail::trim_copy(line.substr(0, pos));
-                std::string item = detail::trim_copy(line.substr(pos + 1));
-                items = detail::split_up(item);
-            } else {
-                name = detail::trim_copy(line);
-                items = {"ON"};
-            }
-
-            if(detail::to_lower(section) == "default")
-                out.fullname = name;
-            else
-                out.fullname = section + "." + name;
-
-            out.inputs.insert(std::end(out.inputs), std::begin(items), std::end(items));
-        }
-    }
-    return output;
-}
-
-/// Parse an INI file, throw an error (ParseError:INIParseError or FileError) on failure
-inline std::vector<ini_ret_t> parse_ini(const std::string &name) {
-
-    std::ifstream input{name};
-    if(!input.good())
-        throw FileError::Missing(name);
-
-    return parse_ini(input);
-}
-
-} // namespace detail
-} // namespace CLI
diff --git a/packages/CLI11/include/CLI/Macros.hpp b/packages/CLI11/include/CLI/Macros.hpp
index 9c10b0814aa82360a3477bdec9b0a8dc3b76d598..6defe59ac20ad376cec7458362fa95116d0a1957 100644
--- a/packages/CLI11/include/CLI/Macros.hpp
+++ b/packages/CLI11/include/CLI/Macros.hpp
@@ -3,12 +3,9 @@
 // Distributed under the 3-Clause BSD License.  See accompanying
 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
 
-namespace CLI {
-
-// Note that all code in CLI11 must be in a namespace, even if it just a define.
+// [CLI11:verbatim]
 
 // The following version macro is very similar to the one in PyBind11
-
 #if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
 #if __cplusplus >= 201402L
 #define CLI11_CPP14
@@ -41,4 +38,4 @@ namespace CLI {
 #define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))
 #endif
 
-} // namespace CLI
+// [CLI11:verbatim]
diff --git a/packages/CLI11/include/CLI/Option.hpp b/packages/CLI11/include/CLI/Option.hpp
index dd61dfd9dad08e129d64201521d82db468787b05..db625576eb8336d9733a5c4a211a4b305711b536 100644
--- a/packages/CLI11/include/CLI/Option.hpp
+++ b/packages/CLI11/include/CLI/Option.hpp
@@ -16,6 +16,7 @@
 #include "CLI/Macros.hpp"
 #include "CLI/Split.hpp"
 #include "CLI/StringTools.hpp"
+#include "CLI/Validators.hpp"
 
 namespace CLI {
 
@@ -34,7 +35,7 @@ template <typename CRTP> class OptionBase {
 
   protected:
     /// The group membership
-    std::string group_{"Options"};
+    std::string group_ = std::string("Options");
 
     /// True if this is a required option
     bool required_{false};
@@ -171,7 +172,9 @@ class Option : public OptionBase<Option> {
     std::string defaultval_;
 
     /// A human readable type value, set when App creates this
-    std::string typeval_;
+    ///
+    /// This is a lambda function so "types" can be dynamic, such as when a set prints its contents.
+    std::function<std::string()> type_name_;
 
     /// True if this option has a default
     bool default_{false};
@@ -207,6 +210,9 @@ class Option : public OptionBase<Option> {
     /// Options store a callback to do all the work
     callback_t callback_;
 
+    /// Options can short-circuit for help options or similar (called before parsing is validated)
+    bool short_circuit_{false};
+
     ///@}
     /// @name Parsing results
     ///@{
@@ -236,8 +242,11 @@ class Option : public OptionBase<Option> {
     /// Count the total number of times an option was passed
     size_t count() const { return results_.size(); }
 
+    /// True if the option was not passed
+    size_t empty() const { return results_.empty(); }
+
     /// This class is true if option is passed.
-    operator bool() const { return count() > 0; }
+    operator bool() const { return !empty(); }
 
     /// Clear the parsed results (mostly for testing)
     void clear() { results_.clear(); }
@@ -250,11 +259,11 @@ class Option : public OptionBase<Option> {
     Option *expected(int value) {
         // Break if this is a flag
         if(type_size_ == 0)
-            throw IncorrectConstruction::SetFlag(single_name());
+            throw IncorrectConstruction::SetFlag(get_name(true, true));
 
         // Setting 0 is not allowed
         else if(value == 0)
-            throw IncorrectConstruction::Set0Opt(single_name());
+            throw IncorrectConstruction::Set0Opt(get_name());
 
         // No change is okay, quit now
         else if(expected_ == value)
@@ -262,16 +271,24 @@ class Option : public OptionBase<Option> {
 
         // Type must be a vector
         else if(type_size_ >= 0)
-            throw IncorrectConstruction::ChangeNotVector(single_name());
+            throw IncorrectConstruction::ChangeNotVector(get_name());
 
         // TODO: Can support multioption for non-1 values (except for join)
         else if(value != 1 && multi_option_policy_ != MultiOptionPolicy::Throw)
-            throw IncorrectConstruction::AfterMultiOpt(single_name());
+            throw IncorrectConstruction::AfterMultiOpt(get_name());
 
         expected_ = value;
         return this;
     }
 
+    /// Adds a validator with a built in type name
+    Option *check(const Validator &validator) {
+        validators_.emplace_back(validator.func);
+        if(!validator.tname.empty())
+            set_type_name(validator.tname);
+        return this;
+    }
+
     /// Adds a validator
     Option *check(std::function<std::string(const std::string &)> validator) {
         validators_.emplace_back(validator);
@@ -291,6 +308,15 @@ class Option : public OptionBase<Option> {
         return this;
     }
 
+    /// Adds a user supplied function to run on each item passed in (communicate though lambda capture)
+    Option *each(std::function<void(std::string)> func) {
+        validators_.emplace_back([func](std::string &inout) {
+            func(inout);
+            return std::string();
+        });
+        return this;
+    }
+
     /// Sets required options
     Option *needs(Option *opt) {
         auto tup = requires_.insert(opt);
@@ -313,26 +339,6 @@ class Option : public OptionBase<Option> {
         return needs(opt1, args...);
     }
 
-#ifndef CLI11_CPP20
-    /// Sets required options \deprecated
-    CLI11_DEPRECATED("Use needs instead of requires (eventual keyword clash)")
-    Option *requires(Option *opt) { return needs(opt); }
-
-    /// Can find a string if needed \deprecated
-    template <typename T = App> Option *requires(std::string opt_name) {
-        for(const Option_p &opt : dynamic_cast<T *>(parent_)->options_)
-            if(opt.get() != this && opt->check_name(opt_name))
-                return needs(opt.get());
-        throw IncorrectConstruction::MissingOption(opt_name);
-    }
-
-    /// Any number supported, any mix of string and Opt \deprecated
-    template <typename A, typename B, typename... ARG> Option *requires(A opt, B opt1, ARG... args) {
-        requires(opt);
-        return requires(opt1, args...);
-    }
-#endif
-
     /// Sets excluded options
     Option *excludes(Option *opt) {
         excludes_.insert(opt);
@@ -376,7 +382,7 @@ class Option : public OptionBase<Option> {
 
         for(const Option_p &opt : parent->options_)
             if(opt.get() != this && *opt == *this)
-                throw OptionAlreadyAdded(opt->get_name());
+                throw OptionAlreadyAdded(opt->get_name(true, true));
 
         return this;
     }
@@ -385,11 +391,19 @@ class Option : public OptionBase<Option> {
     Option *multi_option_policy(MultiOptionPolicy value = MultiOptionPolicy::Throw) {
 
         if(get_items_expected() < 0)
-            throw IncorrectConstruction::MultiOptionPolicy(single_name());
+            throw IncorrectConstruction::MultiOptionPolicy(get_name());
         multi_option_policy_ = value;
         return this;
     }
 
+    /// Options with a short circuit set will run this function before parsing is finished.
+    ///
+    /// This is set on help functions, for example, to escape the normal validation.
+    Option *short_circuit(bool value = true) {
+        short_circuit_ = value;
+        return this;
+    }
+
     ///@}
     /// @name Accessors
     ///@{
@@ -397,6 +411,30 @@ class Option : public OptionBase<Option> {
     /// The number of arguments the option expects
     int get_type_size() const { return type_size_; }
 
+    /// The environment variable associated to this value
+    std::string get_envname() const { return envname_; }
+
+    /// The set of options needed
+    std::set<Option *> get_needs() const { return requires_; }
+
+    /// The set of options excluded
+    std::set<Option *> get_excludes() const { return excludes_; }
+
+    /// The default value (for help printing)
+    std::string get_defaultval() const { return defaultval_; }
+
+    /// See if this is supposed to short circuit (skip validation, INI, etc) (Used for help flags)
+    bool get_short_circuit() const { return short_circuit_; }
+
+    /// Get the callback function
+    callback_t get_callback() const { return callback_; }
+
+    /// Get the long names
+    const std::vector<std::string> get_lnames() const { return lnames_; }
+
+    /// Get the short names
+    const std::vector<std::string> get_snames() const { return snames_; }
+
     /// The number of times the option expects to be included
     int get_expected() const { return expected_; }
 
@@ -436,87 +474,52 @@ class Option : public OptionBase<Option> {
     /// Get the description
     const std::string &get_description() const { return description_; }
 
-    // Just the pname
-    std::string get_pname() const { return pname_; }
-
     ///@}
     /// @name Help tools
     ///@{
 
-    /// Gets a , sep list of names. Does not include the positional name if opt_only=true.
-    std::string get_name(bool opt_only = false) const {
-        std::vector<std::string> name_list;
-        if(!opt_only && pname_.length() > 0)
-            name_list.push_back(pname_);
-        for(const std::string &sname : snames_)
-            name_list.push_back("-" + sname);
-        for(const std::string &lname : lnames_)
-            name_list.push_back("--" + lname);
-        return detail::join(name_list);
-    }
-
-    /// The name and any extras needed for positionals
-    std::string help_positional() const {
-        std::string out = pname_;
-        if(get_expected() > 1)
-            out = out + "(" + std::to_string(get_expected()) + "x)";
-        else if(get_expected() == -1)
-            out = out + "...";
-        out = get_required() ? out : "[" + out + "]";
-        return out;
-    }
-
-    /// The most descriptive name available
-    std::string single_name() const {
-        if(!lnames_.empty())
-            return std::string("--") + lnames_[0];
-        else if(!snames_.empty())
-            return std::string("-") + snames_[0];
-        else
-            return pname_;
-    }
-
-    /// The first half of the help print, name plus default, etc. Setting opt_only to true avoids the positional name.
-    std::string help_name(bool opt_only = false) const {
-        std::stringstream out;
-        out << get_name(opt_only) << help_aftername();
-        return out.str();
-    }
-
-    /// pname with type info
-    std::string help_pname() const {
-        std::stringstream out;
-        out << get_pname() << help_aftername();
-        return out.str();
-    }
-
-    /// This is the part after the name is printed but before the description
-    std::string help_aftername() const {
-        std::stringstream out;
-
-        if(get_type_size() != 0) {
-            if(!typeval_.empty())
-                out << " " << typeval_;
-            if(!defaultval_.empty())
-                out << "=" << defaultval_;
-            if(get_expected() > 1)
-                out << " x " << get_expected();
-            if(get_expected() == -1)
-                out << " ...";
-        }
-        if(!envname_.empty())
-            out << " (env:" << envname_ << ")";
-        if(!requires_.empty()) {
-            out << " Requires:";
-            for(const Option *opt : requires_)
-                out << " " << opt->get_name();
-        }
-        if(!excludes_.empty()) {
-            out << " Excludes:";
-            for(const Option *opt : excludes_)
-                out << " " << opt->get_name();
+    /// \brief Gets a comma seperated list of names.
+    /// Will include / prefer the positional name if positional is true.
+    /// If all_options is false, pick just the most descriptive name to show.
+    /// Use `get_name(true)` to get the positional name (replaces `get_pname`)
+    std::string get_name(bool positional = false, //<[input] Show the positional name
+                         bool all_options = false //<[input] Show every option
+                         ) const {
+
+        if(all_options) {
+
+            std::vector<std::string> name_list;
+
+            /// The all list wil never include a positional unless asked or that's the only name.
+            if((positional && pname_.length()) || (snames_.empty() && lnames_.empty()))
+                name_list.push_back(pname_);
+
+            for(const std::string &sname : snames_)
+                name_list.push_back("-" + sname);
+
+            for(const std::string &lname : lnames_)
+                name_list.push_back("--" + lname);
+
+            return detail::join(name_list);
+
+        } else {
+
+            // This returns the positional name no matter what
+            if(positional)
+                return pname_;
+
+            // Prefer long name
+            else if(!lnames_.empty())
+                return std::string("--") + lnames_[0];
+
+            // Or short name if no long name
+            else if(!snames_.empty())
+                return std::string("-") + snames_[0];
+
+            // If positional is the only name, it's okay to use that
+            else
+                return pname_;
         }
-        return out.str();
     }
 
     ///@}
@@ -532,7 +535,7 @@ class Option : public OptionBase<Option> {
                 for(const std::function<std::string(std::string &)> &vali : validators_) {
                     std::string err_msg = vali(result);
                     if(!err_msg.empty())
-                        throw ValidationError(single_name(), err_msg);
+                        throw ValidationError(get_name(), err_msg);
                 }
         }
 
@@ -560,7 +563,7 @@ class Option : public OptionBase<Option> {
             // For now, vector of non size 1 types are not supported but possibility included here
             if((get_items_expected() > 0 && results_.size() != static_cast<size_t>(get_items_expected())) ||
                (get_items_expected() < 0 && results_.size() < static_cast<size_t>(-get_items_expected())))
-                throw ArgumentMismatch(single_name(), get_items_expected(), results_.size());
+                throw ArgumentMismatch(get_name(), get_items_expected(), results_.size());
             else
                 local_result = !callback_(results_);
         }
@@ -626,12 +629,18 @@ class Option : public OptionBase<Option> {
             return std::find(std::begin(lnames_), std::end(lnames_), name) != std::end(lnames_);
     }
 
-    /// Puts a result at the end, unless last_ is set, in which case it just keeps the last one
+    /// Puts a result at the end
     void add_result(std::string s) {
         results_.push_back(s);
         callback_run_ = false;
     }
 
+    /// Set the results vector all at once
+    void set_results(std::vector<std::string> results) {
+        results_ = results;
+        callback_run_ = false;
+    }
+
     /// Get a copy of the results
     std::vector<std::string> results() const { return results_; }
 
@@ -644,7 +653,7 @@ class Option : public OptionBase<Option> {
 
     /// Set a custom option, typestring, type_size
     void set_custom_option(std::string typeval, int type_size = 1) {
-        typeval_ = typeval;
+        set_type_name(typeval);
         type_size_ = type_size;
         if(type_size_ == 0)
             required_ = false;
@@ -665,21 +674,15 @@ class Option : public OptionBase<Option> {
     }
 
     /// Set the type name displayed on this option
-    void set_type_name(std::string val) { typeval_ = val; }
-
-    /// Get the typename for this option
-    std::string get_type_name() const { return typeval_; }
+    void set_type_name(std::string typeval) {
+        set_type_name_fn([typeval]() { return typeval; });
+    }
 
-    ///@}
+    /// Set the type function to run when displayed on this option
+    void set_type_name_fn(std::function<std::string()> typefun) { type_name_ = typefun; }
 
-  protected:
-    /// @name App Helpers
-    ///@{
-    /// Can print positional name detailed option if true
-    bool _has_help_positional() const {
-        return get_positional() && (has_description() || !requires_.empty() || !excludes_.empty());
-    }
-    ///@}
+    /// Get the typename for this option
+    std::string get_type_name() const { return type_name_(); }
 };
 
 } // namespace CLI
diff --git a/packages/CLI11/include/CLI/Optional.hpp b/packages/CLI11/include/CLI/Optional.hpp
index 1597da336a45a3f9944dd5d8ee5f0a50f20b79de..faaee3664c0ceee61d5d78b9bf19fe917e7de8f3 100644
--- a/packages/CLI11/include/CLI/Optional.hpp
+++ b/packages/CLI11/include/CLI/Optional.hpp
@@ -9,32 +9,40 @@
 
 // [CLI11:verbatim]
 #ifdef __has_include
-#if defined(CLI11_CPP17) && __has_include(<optional>)
-#include <optional>
-#ifdef __cpp_lib_optional
-#ifndef CLI11_STD_OPTIONAL
-#define CLI11_STD_OPTIONAL
+
+#if defined(CLI11_CPP17) && __has_include(<optional>) && \
+     !defined(CLI11_STD_OPTIONAL)
+#define CLI11_STD_OPTIONAL 1
 #endif
+
+#if defined(CLI11_CPP14) && __has_include(<experimental/optional>) && \
+    !defined(CLI11_EXPERIMENTAL_OPTIONAL)
+#define CLI11_EXPERIMENTAL_OPTIONAL 1
 #endif
+
+#if __has_include(<boost/optional.hpp>) && !defined(CLI11_BOOST_OPTIONAL)
+#include <boost/version.hpp>
+#if BOOST_VERSION >= 105800
+#define CLI11_BOOST_OPTIONAL 1
 #endif
-#if defined(CLI11_CPP14) && __has_include(<experimental/optional>)
-#include <experimental/optional>
-#ifndef CLI11_EXPERIMENTAL_OPTIONAL
-#define CLI11_EXPERIMENTAL_OPTIONAL
 #endif
+
 #endif
-#if __has_include(<boost/optional.hpp>)
-#include <boost/optional.hpp>
-#ifndef CLI11_BOOST_OPTIONAL
-#define CLI11_BOOST_OPTIONAL
+
+#if CLI11_STD_OPTIONAL
+#include <optional>
 #endif
+#if CLI11_EXPERIMENTAL_OPTIONAL
+#include <experimental/optional>
 #endif
+#if CLI11_BOOST_OPTIONAL
+#include <boost/optional.hpp>
 #endif
 // [CLI11:verbatim]
 
 namespace CLI {
 
-#ifdef CLI11_STD_OPTIONAL
+#if CLI11_STD_OPTIONAL
 template <typename T> std::istream &operator>>(std::istream &in, std::optional<T> &val) {
     T v;
     in >> v;
@@ -43,7 +51,7 @@ template <typename T> std::istream &operator>>(std::istream &in, std::optional<T
 }
 #endif
 
-#ifdef CLI11_EXPERIMENTAL_OPTIONAL
+#if CLI11_EXPERIMENTAL_OPTIONAL
 template <typename T> std::istream &operator>>(std::istream &in, std::experimental::optional<T> &val) {
     T v;
     in >> v;
@@ -52,7 +60,7 @@ template <typename T> std::istream &operator>>(std::istream &in, std::experiment
 }
 #endif
 
-#ifdef CLI11_BOOST_OPTIONAL
+#if CLI11_BOOST_OPTIONAL
 template <typename T> std::istream &operator>>(std::istream &in, boost::optional<T> &val) {
     T v;
     in >> v;
@@ -62,17 +70,17 @@ template <typename T> std::istream &operator>>(std::istream &in, boost::optional
 #endif
 
 // Export the best optional to the CLI namespace
-#if defined(CLI11_STD_OPTIONAL)
+#if CLI11_STD_OPTIONAL
 using std::optional;
-#elif defined(CLI11_EXPERIMENTAL_OPTIONAL)
+#elif CLI11_EXPERIMENTAL_OPTIONAL
 using std::experimental::optional;
-#elif defined(CLI11_BOOST_OPTIONAL)
+#elif CLI11_BOOST_OPTIONAL
 using boost::optional;
 #endif
 
 // This is true if any optional is found
-#if defined(CLI11_STD_OPTIONAL) || defined(CLI11_EXPERIMENTAL_OPTIONAL) || defined(CLI11_BOOST_OPTIONAL)
-#define CLI11_OPTIONAL
+#if CLI11_STD_OPTIONAL || CLI11_EXPERIMENTAL_OPTIONAL || CLI11_BOOST_OPTIONAL
+#define CLI11_OPTIONAL 1
 #endif
 
 } // namespace CLI
diff --git a/packages/CLI11/include/CLI/StringTools.hpp b/packages/CLI11/include/CLI/StringTools.hpp
index ded9eaa6cce671518eff662f2aa50c53f1432f2e..24e01bcd7190ca3393fbc148e964b8e280f3d3dc 100644
--- a/packages/CLI11/include/CLI/StringTools.hpp
+++ b/packages/CLI11/include/CLI/StringTools.hpp
@@ -8,8 +8,8 @@
 #include <locale>
 #include <sstream>
 #include <string>
-#include <vector>
 #include <type_traits>
+#include <vector>
 
 namespace CLI {
 namespace detail {
@@ -104,15 +104,16 @@ inline std::string trim_copy(const std::string &str, const std::string &filter)
     return trim(s, filter);
 }
 /// Print a two part "help" string
-inline void format_help(std::stringstream &out, std::string name, std::string description, size_t wid) {
+inline std::ostream &format_help(std::ostream &out, std::string name, std::string description, size_t wid) {
     name = "  " + name;
     out << std::setw(static_cast<int>(wid)) << std::left << name;
     if(!description.empty()) {
         if(name.length() >= wid)
-            out << std::endl << std::setw(static_cast<int>(wid)) << "";
+            out << "\n" << std::setw(static_cast<int>(wid)) << "";
         out << description;
     }
-    out << std::endl;
+    out << "\n";
+    return out;
 }
 
 /// Verify the first character of an option
diff --git a/packages/CLI11/include/CLI/TypeTools.hpp b/packages/CLI11/include/CLI/TypeTools.hpp
index aea4b8568796e94a690a16f7d979357167a17cf1..9af8ccaa62d264482599ad14788033dfe17b8ecf 100644
--- a/packages/CLI11/include/CLI/TypeTools.hpp
+++ b/packages/CLI11/include/CLI/TypeTools.hpp
@@ -137,15 +137,7 @@ template <typename T,
                           !std::is_assignable<T &, std::string>::value,
                       detail::enabler> = detail::dummy>
 bool lexical_cast(std::string input, T &output) {
-
-// On GCC 4.7, thread_local is not available, so this optimization
-// is turned off (avoiding multiple initialisations on multiple usages)
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && __GNUC__ == 4 && (__GNUC_MINOR__ < 8)
     std::istringstream is;
-#else
-    static thread_local std::istringstream is;
-    is.clear();
-#endif
 
     is.str(input);
     is >> output;
diff --git a/packages/CLI11/include/CLI/Validators.hpp b/packages/CLI11/include/CLI/Validators.hpp
index f158f92782b8ab74bbf40b2e2a69b7deaad42a2d..6c41967e4d8a0075a86331350df994a0de93480d 100644
--- a/packages/CLI11/include/CLI/Validators.hpp
+++ b/packages/CLI11/include/CLI/Validators.hpp
@@ -18,74 +18,171 @@
 namespace CLI {
 
 /// @defgroup validator_group Validators
+
 /// @brief Some validators that are provided
 ///
-/// These are simple `void(std::string&)` validators that are useful. They throw
-/// a ValidationError if they fail (or the normally expected error if the cast fails)
+/// These are simple `std::string(const std::string&)` validators that are useful. They return
+/// a string if the validation fails. A custom struct is provided, as well, with the same user
+/// semantics, but with the ability to provide a new type name.
 /// @{
 
-/// Check for an existing file
-inline std::string ExistingFile(const std::string &filename) {
-    struct stat buffer;
-    bool exist = stat(filename.c_str(), &buffer) == 0;
-    bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
-    if(!exist) {
-        return "File does not exist: " + filename;
-    } else if(is_dir) {
-        return "File is actually a directory: " + filename;
+///
+struct Validator {
+    /// This is the type name, if empty the type name will not be changed
+    std::string tname;
+    std::function<std::string(const std::string &filename)> func;
+
+    /// This is the required operator for a validator - provided to help
+    /// users (CLI11 uses the func directly)
+    std::string operator()(const std::string &filename) const { return func(filename); };
+
+    /// Combining validators is a new validator
+    Validator operator&(const Validator &other) const {
+        Validator newval;
+        newval.tname = (tname == other.tname ? tname : "");
+
+        // Give references (will make a copy in lambda function)
+        const std::function<std::string(const std::string &filename)> &f1 = func;
+        const std::function<std::string(const std::string &filename)> &f2 = other.func;
+
+        newval.func = [f1, f2](const std::string &filename) {
+            std::string s1 = f1(filename);
+            std::string s2 = f2(filename);
+            if(!s1.empty() && !s2.empty())
+                return s1 + " & " + s2;
+            else
+                return s1 + s2;
+        };
+        return newval;
+    }
+
+    /// Combining validators is a new validator
+    Validator operator|(const Validator &other) const {
+        Validator newval;
+        newval.tname = (tname == other.tname ? tname : "");
+
+        // Give references (will make a copy in lambda function)
+        const std::function<std::string(const std::string &filename)> &f1 = func;
+        const std::function<std::string(const std::string &filename)> &f2 = other.func;
+
+        newval.func = [f1, f2](const std::string &filename) {
+            std::string s1 = f1(filename);
+            std::string s2 = f2(filename);
+            if(s1.empty() || s2.empty())
+                return std::string();
+            else
+                return s1 + " & " + s2;
+        };
+        return newval;
+    }
+};
+
+// The implementation of the built in validators is using the Validator class;
+// the user is only expected to use the const (static) versions (since there's no setup).
+// Therefore, this is in detail.
+namespace detail {
+
+/// Check for an existing file (returns error message if check fails)
+struct ExistingFileValidator : public Validator {
+    ExistingFileValidator() {
+        tname = "FILE";
+        func = [](const std::string &filename) {
+            struct stat buffer;
+            bool exist = stat(filename.c_str(), &buffer) == 0;
+            bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
+            if(!exist) {
+                return "File does not exist: " + filename;
+            } else if(is_dir) {
+                return "File is actually a directory: " + filename;
+            }
+            return std::string();
+        };
     }
-    return std::string();
-}
-
-/// Check for an existing directory
-inline std::string ExistingDirectory(const std::string &filename) {
-    struct stat buffer;
-    bool exist = stat(filename.c_str(), &buffer) == 0;
-    bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
-    if(!exist) {
-        return "Directory does not exist: " + filename;
-    } else if(!is_dir) {
-        return "Directory is actually a file: " + filename;
+};
+
+/// Check for an existing directory (returns error message if check fails)
+struct ExistingDirectoryValidator : public Validator {
+    ExistingDirectoryValidator() {
+        tname = "DIR";
+        func = [](const std::string &filename) {
+            struct stat buffer;
+            bool exist = stat(filename.c_str(), &buffer) == 0;
+            bool is_dir = (buffer.st_mode & S_IFDIR) != 0;
+            if(!exist) {
+                return "Directory does not exist: " + filename;
+            } else if(!is_dir) {
+                return "Directory is actually a file: " + filename;
+            }
+            return std::string();
+        };
     }
-    return std::string();
-}
+};
 
 /// Check for an existing path
-inline std::string ExistingPath(const std::string &filename) {
-    struct stat buffer;
-    bool const exist = stat(filename.c_str(), &buffer) == 0;
-    if(!exist) {
-        return "Path does not exist: " + filename;
+struct ExistingPathValidator : public Validator {
+    ExistingPathValidator() {
+        tname = "PATH";
+        func = [](const std::string &filename) {
+            struct stat buffer;
+            bool const exist = stat(filename.c_str(), &buffer) == 0;
+            if(!exist) {
+                return "Path does not exist: " + filename;
+            }
+            return std::string();
+        };
     }
-    return std::string();
-}
-
-/// Check for a non-existing path
-inline std::string NonexistentPath(const std::string &filename) {
-    struct stat buffer;
-    bool exist = stat(filename.c_str(), &buffer) == 0;
-    if(exist) {
-        return "Path already exists: " + filename;
+};
+
+/// Check for an non-existing path
+struct NonexistentPathValidator : public Validator {
+    NonexistentPathValidator() {
+        tname = "PATH";
+        func = [](const std::string &filename) {
+            struct stat buffer;
+            bool exist = stat(filename.c_str(), &buffer) == 0;
+            if(exist) {
+                return "Path already exists: " + filename;
+            }
+            return std::string();
+        };
     }
-    return std::string();
-}
-
-/// Produce a range validator function
-template <typename T> std::function<std::string(const std::string &)> Range(T min, T max) {
-    return [min, max](std::string input) {
-        T val;
-        detail::lexical_cast(input, val);
-        if(val < min || val > max)
-            return "Value " + input + " not in range " + std::to_string(min) + " to " + std::to_string(max);
-
-        return std::string();
-    };
-}
-
-/// Range of one value is 0 to value
-template <typename T> std::function<std::string(const std::string &)> Range(T max) {
-    return Range(static_cast<T>(0), max);
-}
+};
+} // namespace detail
+
+// Static is not needed here, because global const implies static.
+
+/// Check for existing file (returns error message if check fails)
+const detail::ExistingFileValidator ExistingFile;
+
+/// Check for an existing directory (returns error message if check fails)
+const detail::ExistingDirectoryValidator ExistingDirectory;
+
+/// Check for an existing path
+const detail::ExistingPathValidator ExistingPath;
+
+/// Check for an non-existing path
+const detail::NonexistentPathValidator NonexistentPath;
+
+///  Produce a range (factory). Min and max are inclusive.
+struct Range : public Validator {
+    template <typename T> Range(T min, T max) {
+        std::stringstream out;
+        out << detail::type_name<T>() << " in [" << min << " - " << max << "]";
+
+        tname = out.str();
+        func = [min, max](std::string input) {
+            T val;
+            detail::lexical_cast(input, val);
+            if(val < min || val > max)
+                return "Value " + input + " not in range " + std::to_string(min) + " to " + std::to_string(max);
+
+            return std::string();
+        };
+    }
+
+    /// Range of one value is 0 to value
+    template <typename T> explicit Range(T max) : Range(static_cast<T>(0), max) {}
+};
 
 /// @}
 
diff --git a/packages/CLI11/include/CLI/Version.hpp b/packages/CLI11/include/CLI/Version.hpp
index 009b3f875c0ce2f400335c9e1c34db9b8ad28e37..49393847c71d7d0d48580d290faa70ea6d00c003 100644
--- a/packages/CLI11/include/CLI/Version.hpp
+++ b/packages/CLI11/include/CLI/Version.hpp
@@ -3,13 +3,11 @@
 // Distributed under the 3-Clause BSD License.  See accompanying
 // file LICENSE or https://github.com/CLIUtils/CLI11 for details.
 
-namespace CLI {
-
-// Note that all code in CLI11 must be in a namespace, even if it just a define.
+// [CLI11:verbatim]
 
 #define CLI11_VERSION_MAJOR 1
 #define CLI11_VERSION_MINOR 5
-#define CLI11_VERSION_PATCH 0
-#define CLI11_VERSION "1.5.0"
+#define CLI11_VERSION_PATCH 4
+#define CLI11_VERSION "1.5.4"
 
-} // namespace CLI
+// [CLI11:verbatim]
diff --git a/packages/CLI11/scripts/MakeSingleHeader.py b/packages/CLI11/scripts/MakeSingleHeader.py
index d260ab959024a04c52871a0bc3ae6c9b44b143f5..bb83e8cdeaa7254ed2aa80b13c5fa8a11fb59a4d 100755
--- a/packages/CLI11/scripts/MakeSingleHeader.py
+++ b/packages/CLI11/scripts/MakeSingleHeader.py
@@ -12,6 +12,7 @@ from functools import reduce
 
 includes_local = re.compile(r"""^#include "(.*)"$""", re.MULTILINE)
 includes_system = re.compile(r"""^#include \<(.*)\>$""", re.MULTILINE)
+version_finder = re.compile(r"""^#define CLI11_VERSION \"(.*)\"$""", re.MULTILINE)
 verbatim_tag_str = r"""
 ^               # Begin of line
 [^\n^\[]+       # Some characters, not including [ or the end of a line
@@ -30,14 +31,20 @@ DIR = os.path.dirname(os.path.abspath(__file__))
 
 class HeaderFile(object):
     TAG = "Unknown git revision"
+    LICENSE = "// BSD 3 clause"
+    VERSION = "Unknown"
 
     def __init__(self, base, inc):
         with open(os.path.join(base, inc)) as f:
             inner = f.read()
 
+        version = version_finder.search(inner)
+        if version:
+            self.__class__.VERSION = version.groups()[0]
+
         # add self.verbatim
         if 'CLI11:verbatim' in inner:
-            self.verbatim = ["\n\n// Verbatim copy from {}".format(inc)]
+            self.verbatim = ["\n\n// Verbatim copy from {}:".format(inc)]
             self.verbatim += verbatim_all.findall(inner)
             inner = verbatim_all.sub("", inner)
         else:
@@ -45,7 +52,7 @@ class HeaderFile(object):
 
         self.headers = set(includes_system.findall(inner))
 
-        self.body = '\n// From {}\n\n'.format(inc) + inner[inner.find('namespace'):]
+        self.body = '\n// From {}:\n\n'.format(inc) + inner[inner.find('namespace'):]
 
     def __add__(self, other):
         out = copy(self)
@@ -66,12 +73,18 @@ class HeaderFile(object):
         return '''\
 #pragma once
 
-// Distributed under the 3-Clause BSD License.  See accompanying
-// file LICENSE or https://github.com/CLIUtils/CLI11 for details.
-
-// This file was generated using MakeSingleHeader.py in CLI11/scripts
+// CLI11: Version {self.VERSION}
+// Originally designed by Henry Schreiner
+// https://github.com/CLIUtils/CLI11
+//
+// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
 // from: {self.TAG}
-// This has the complete CLI library in one file.
+//
+// From LICENSE:
+//
+{self.LICENSE}
+
+// Standard combined includes:
 
 {self.header_str}
 {self.verbatim_str}
@@ -82,12 +95,16 @@ class HeaderFile(object):
 def MakeHeader(output, main_header, include_dir = '../include'):
     # Set tag if possible to class variable
     try:
-        HeaderFile.TAG = check_output(['git', 'describe', '--tags', '--always'], cwd=str(DIR)).decode("utf-8")
+        HeaderFile.TAG = check_output(['git', 'describe', '--tags', '--always'], cwd=str(DIR)).decode("utf-8").strip()
     except CalledProcessError:
         pass
 
     base_dir = os.path.abspath(os.path.join(DIR, include_dir))
     main_header = os.path.join(base_dir, main_header)
+    licence_file = os.path.abspath(os.path.join(DIR, '../LICENSE'))
+
+    with open(licence_file) as f:
+        HeaderFile.LICENSE = ''.join('// ' + line for line in f)
 
     with open(main_header) as f:
         header = f.read()
diff --git a/packages/CLI11/scripts/check_style.sh b/packages/CLI11/scripts/check_style.sh
index 3fe500ec60d64047cba9ae4deac09404fb11319d..a51bc7f6995ea6655f4839fcc6859d9f5d326a5d 100755
--- a/packages/CLI11/scripts/check_style.sh
+++ b/packages/CLI11/scripts/check_style.sh
@@ -3,7 +3,7 @@ set -evx
 
 clang-format --version
 
-git ls-files -- '*.cpp' '*.hpp' | xargs clang-format -i -style=file
+git ls-files -- '*.cpp' '*.hpp' | xargs clang-format -sort-includes -i -style=file
 
 git diff --exit-code --color
 
diff --git a/packages/CLI11/scripts/clang-format-pre-commit b/packages/CLI11/scripts/clang-format-pre-commit
index 822becb06f82c2a0cd8bac4bdc382ed1265384fb..96e178fd2381168895b200d7e76e5c7ece1c6cb6 100755
--- a/packages/CLI11/scripts/clang-format-pre-commit
+++ b/packages/CLI11/scripts/clang-format-pre-commit
@@ -9,9 +9,15 @@ format_file() {
     file="${1}"
     case "$file" in
     *.hpp | *.cpp | .c | *.cc | *.cu | *.h )
-        echo "Fixing: $file"
-        clang-format -i -style=file "${1}"
-        git add "${1}"
+        if [ -f "${1}" ] ; then
+            clang-format -i -style=file -sort-includes "${1}"
+            if git diff-files --quiet -- "${1}" ; then
+                echo "Already nicely formatted: ${1}"
+            else
+                git add "${1}"
+                echo "Reformatting file: ${1}"
+            fi
+        fi
         ;;
     *)
         ;;
diff --git a/packages/CLI11/tests/AppTest.cpp b/packages/CLI11/tests/AppTest.cpp
index 5b1e0d92581a7255ec5f9a58040e8d08dab64cc7..76537e14ea23858e47bd2f6813b88738dfdcb085 100644
--- a/packages/CLI11/tests/AppTest.cpp
+++ b/packages/CLI11/tests/AppTest.cpp
@@ -1,6 +1,6 @@
 #include "app_helper.hpp"
-#include <cstdlib>
 #include <complex>
+#include <cstdlib>
 
 TEST_F(TApp, OneFlagShort) {
     app.add_flag("-c,--count");
@@ -992,6 +992,21 @@ TEST_F(TApp, FailSet) {
     EXPECT_THROW(run(), CLI::ConversionError);
 }
 
+TEST_F(TApp, FailLValueSet) {
+
+    int choice;
+    std::set<int> vals{1, 2, 3};
+    app.add_set("-q,--quick", choice, vals);
+    app.add_set("-s,--slow", choice, vals, "", true);
+
+    args = {"--quick=hello"};
+    EXPECT_THROW(run(), CLI::ConversionError);
+
+    app.reset();
+    args = {"--slow=hello"};
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
 TEST_F(TApp, InSetIgnoreCase) {
 
     std::string choice;
@@ -1234,43 +1249,6 @@ TEST_F(TApp, NeedsMixedFlags) {
     run();
 }
 
-#ifndef CLI11_CPP20
-
-TEST_F(TApp, RequiresMixedFlags) {
-    CLI::Option *opt1 = app.add_flag("--opt1");
-    app.add_flag("--opt2");
-    app.add_flag("--opt3");
-    app.add_flag("--optall")->requires(opt1, "--opt2", "--opt3");
-
-    run();
-
-    app.reset();
-    args = {"--opt1"};
-    run();
-
-    app.reset();
-    args = {"--opt2"};
-    run();
-
-    app.reset();
-    args = {"--optall"};
-    EXPECT_THROW(run(), CLI::RequiresError);
-
-    app.reset();
-    args = {"--optall", "--opt1"};
-    EXPECT_THROW(run(), CLI::RequiresError);
-
-    app.reset();
-    args = {"--optall", "--opt2", "--opt1"};
-    EXPECT_THROW(run(), CLI::RequiresError);
-
-    app.reset();
-    args = {"--optall", "--opt1", "--opt2", "--opt3"};
-    run();
-}
-
-#endif
-
 TEST_F(TApp, NeedsChainedFlags) {
     CLI::Option *opt1 = app.add_flag("--opt1");
     CLI::Option *opt2 = app.add_flag("--opt2")->needs(opt1);
@@ -1500,6 +1478,23 @@ TEST_F(TApp, ThrowingTransform) {
     }
 }
 
+// This was added to make running a simple function on each item easier
+TEST_F(TApp, EachItem) {
+
+    std::vector<std::string> results;
+    std::vector<std::string> dummy;
+
+    auto opt = app.add_option("--vec", dummy);
+
+    opt->each([&results](std::string item) { results.push_back(item); });
+
+    args = {"--vec", "one", "two", "three"};
+
+    run();
+
+    EXPECT_EQ(results, dummy);
+}
+
 // #87
 TEST_F(TApp, CustomDoubleOption) {
 
@@ -1517,3 +1512,72 @@ TEST_F(TApp, CustomDoubleOption) {
     EXPECT_EQ(custom_opt.first, 12);
     EXPECT_DOUBLE_EQ(custom_opt.second, 1.5);
 }
+
+// #113
+TEST_F(TApp, AddRemoveSetItems) {
+    std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
+
+    std::string type1, type2;
+    app.add_set("--type1", type1, items);
+    app.add_set("--type2", type2, items, "", true);
+
+    args = {"--type1", "TYPE1", "--type2", "TYPE2"};
+
+    run();
+    EXPECT_EQ(type1, "TYPE1");
+    EXPECT_EQ(type2, "TYPE2");
+
+    items.insert("TYPE6");
+    items.insert("TYPE7");
+
+    items.erase("TYPE1");
+    items.erase("TYPE2");
+
+    app.reset();
+    args = {"--type1", "TYPE6", "--type2", "TYPE7"};
+    run();
+    EXPECT_EQ(type1, "TYPE6");
+    EXPECT_EQ(type2, "TYPE7");
+
+    app.reset();
+    args = {"--type1", "TYPE1"};
+    EXPECT_THROW(run(), CLI::ConversionError);
+
+    app.reset();
+    args = {"--type2", "TYPE2"};
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
+
+TEST_F(TApp, AddRemoveSetItemsNoCase) {
+    std::set<std::string> items{"TYPE1", "TYPE2", "TYPE3", "TYPE4", "TYPE5"};
+
+    std::string type1, type2;
+    app.add_set_ignore_case("--type1", type1, items);
+    app.add_set_ignore_case("--type2", type2, items, "", true);
+
+    args = {"--type1", "TYPe1", "--type2", "TyPE2"};
+
+    run();
+    EXPECT_EQ(type1, "TYPE1");
+    EXPECT_EQ(type2, "TYPE2");
+
+    items.insert("TYPE6");
+    items.insert("TYPE7");
+
+    items.erase("TYPE1");
+    items.erase("TYPE2");
+
+    app.reset();
+    args = {"--type1", "TyPE6", "--type2", "tYPE7"};
+    run();
+    EXPECT_EQ(type1, "TYPE6");
+    EXPECT_EQ(type2, "TYPE7");
+
+    app.reset();
+    args = {"--type1", "TYPe1"};
+    EXPECT_THROW(run(), CLI::ConversionError);
+
+    app.reset();
+    args = {"--type2", "TYpE2"};
+    EXPECT_THROW(run(), CLI::ConversionError);
+}
diff --git a/packages/CLI11/tests/CMakeLists.txt b/packages/CLI11/tests/CMakeLists.txt
index 4c191fcc4e4a7e518f2a1539e49142e77be1cca2..81d9c118746b6872bf7919a0ac685b4767c53842 100644
--- a/packages/CLI11/tests/CMakeLists.txt
+++ b/packages/CLI11/tests/CMakeLists.txt
@@ -1,3 +1,21 @@
+if(NOT EXISTS "${CLI11_SOURCE_DIR}/extern/googletest/CMakeLists.txt")
+    message(FATAL_ERROR "You have requested tests be built, but googletest is not downloaded. Please run:
+    git submodule update --init")
+endif()
+
+# If submodule is available, add sanitizers
+# Set SANITIZE_ADDRESS, SANITIZE_MEMORY, SANITIZE_THREAD or SANITIZE_UNDEFINED
+if(EXISTS "${CLI11_SOURCE_DIR}/extern/sanitizers/cmake/FindSanitizers.cmake")
+    set(CMAKE_MODULE_PATH "${CLI11_SOURCE_DIR}/extern/sanitizers/cmake" ${CMAKE_MODULE_PATH})
+    find_package(Sanitizers)
+    if(SANITIZE_ADDRESS)
+        message(STATUS "You might want to use \"${ASan_WRAPPER}\" to run your program")
+    endif()
+else()
+    macro(add_sanitizers)
+    endmacro()
+endif()
+
 set(GOOGLE_TEST_INDIVIDUAL OFF)
 include(AddGoogletest)
 
@@ -9,6 +27,7 @@ set(CLI11_TESTS
     CreationTest
     SubcommandTest
     HelpTest
+    FormatterTest
     NewParseTest
     OptionalTest
     )
@@ -23,6 +42,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 foreach(T ${CLI11_TESTS})
 
     add_executable(${T} ${T}.cpp ${CLI11_headers})
+    add_sanitizers(${T})
     target_link_libraries(${T} PUBLIC CLI11)
     add_gtest(${T})
 
@@ -40,6 +60,7 @@ endforeach()
 foreach(T ${CLI11_MULTIONLY_TESTS})
 
     add_executable(${T} ${T}.cpp ${CLI11_headers})
+    add_sanitizers(${T})
     target_link_libraries(${T} PUBLIC CLI11)
     add_gtest(${T})
 
@@ -72,7 +93,7 @@ file(WRITE "${PROJECT_BINARY_DIR}/CTestCustom.cmake"
     )
 
 # Add boost to test boost::optional if available
-find_package(Boost 1.35)
+find_package(Boost 1.58)
 if(Boost_FOUND)
     target_link_libraries(informational PUBLIC Boost::boost)
     target_link_libraries(OptionalTest PUBLIC Boost::boost)
@@ -82,3 +103,13 @@ if(Boost_FOUND)
     target_compile_definitions(OptionalTest PUBLIC CLI11_BOOST_OPTIONAL)
 endif()
 
+if(CMAKE_BUILD_TYPE STREQUAL Coverage)
+    include(CodeCoverage)
+    setup_target_for_coverage(
+        NAME CLI11_coverage
+        EXECUTABLE ctest
+        DEPENDENCIES
+          ${CLI11_TESTS}
+          ${CLI11_MULTIONLY_TESTS})
+endif()
+
diff --git a/packages/CLI11/tests/CreationTest.cpp b/packages/CLI11/tests/CreationTest.cpp
index cee6d3bddfaa18f64d9b41ed35f8b83cf28d2402..72c00bd3c8ccec192f67139222915b70593f9dcc 100644
--- a/packages/CLI11/tests/CreationTest.cpp
+++ b/packages/CLI11/tests/CreationTest.cpp
@@ -2,7 +2,10 @@
 #include <cstdlib>
 
 TEST_F(TApp, AddingExistingShort) {
-    app.add_flag("-c,--count");
+    CLI::Option *opt = app.add_flag("-c,--count");
+    EXPECT_EQ(opt->get_lnames(), std::vector<std::string>({"count"}));
+    EXPECT_EQ(opt->get_snames(), std::vector<std::string>({"c"}));
+
     EXPECT_THROW(app.add_flag("--cat,-c"), CLI::OptionAlreadyAdded);
 }
 
@@ -75,7 +78,7 @@ TEST_F(TApp, RecoverSubcommands) {
     CLI::App *app3 = app.add_subcommand("app3");
     CLI::App *app4 = app.add_subcommand("app4");
 
-    EXPECT_EQ(app.get_subcommands(false), std::vector<CLI::App *>({app1, app2, app3, app4}));
+    EXPECT_EQ(app.get_subcommands({}), std::vector<CLI::App *>({app1, app2, app3, app4}));
 }
 
 TEST_F(TApp, MultipleSubcomMatchingWithCase) {
@@ -168,13 +171,6 @@ TEST_F(TApp, IncorrectConstructionNeedsCannotFind) {
     EXPECT_THROW(cat->needs("--nothing"), CLI::IncorrectConstruction);
 }
 
-#ifndef CLI11_CPP20
-TEST_F(TApp, IncorrectConstructionRequiresCannotFind) {
-    auto cat = app.add_flag("--cat");
-    EXPECT_THROW(cat->requires("--nothing"), CLI::IncorrectConstruction);
-}
-#endif
-
 TEST_F(TApp, IncorrectConstructionExcludesCannotFind) {
     auto cat = app.add_flag("--cat");
     EXPECT_THROW(cat->excludes("--nothing"), CLI::IncorrectConstruction);
@@ -331,13 +327,22 @@ TEST_F(TApp, OptionFromDefaultsSubcommands) {
     EXPECT_EQ(app2->option_defaults()->get_group(), "Something");
 }
 
-TEST_F(TApp, HelpFlagFromDefaultsSubcommands) {
-    app.set_help_flag("--that", "Wow");
+TEST_F(TApp, GetNameCheck) {
+    int x;
+    auto a = app.add_flag("--that");
+    auto b = app.add_flag("-x");
+    auto c = app.add_option("pos", x);
+    auto d = app.add_option("one,-o,--other", x);
 
-    auto app2 = app.add_subcommand("app2");
+    EXPECT_EQ(a->get_name(false, true), "--that");
+    EXPECT_EQ(b->get_name(false, true), "-x");
+    EXPECT_EQ(c->get_name(false, true), "pos");
 
-    EXPECT_EQ(app2->get_help_ptr()->get_name(), "--that");
-    EXPECT_EQ(app2->get_help_ptr()->get_description(), "Wow");
+    EXPECT_EQ(d->get_name(), "--other");
+    EXPECT_EQ(d->get_name(false, false), "--other");
+    EXPECT_EQ(d->get_name(false, true), "-o,--other");
+    EXPECT_EQ(d->get_name(true, true), "one,-o,--other");
+    EXPECT_EQ(d->get_name(true, false), "one");
 }
 
 TEST_F(TApp, SubcommandDefaults) {
@@ -404,3 +409,15 @@ TEST_F(TApp, SubcommandMinMax) {
     EXPECT_EQ(app.get_require_subcommand_min(), (size_t)3);
     EXPECT_EQ(app.get_require_subcommand_max(), (size_t)7);
 }
+
+TEST_F(TApp, GetOptionList) {
+    int two;
+    auto flag = app.add_flag("--one");
+    auto opt = app.add_option("--two", two);
+
+    auto opt_list = app.get_options();
+
+    ASSERT_EQ(opt_list.size(), static_cast<size_t>(3));
+    EXPECT_EQ(opt_list.at(1), flag);
+    EXPECT_EQ(opt_list.at(2), opt);
+}
diff --git a/packages/CLI11/tests/FormatterTest.cpp b/packages/CLI11/tests/FormatterTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..74af18b9e8dd1a4a819b7eb5485f02de265e40f9
--- /dev/null
+++ b/packages/CLI11/tests/FormatterTest.cpp
@@ -0,0 +1,136 @@
+#ifdef CLI11_SINGLE_FILE
+#include "CLI11.hpp"
+#else
+#include "CLI/CLI.hpp"
+#endif
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <fstream>
+
+using ::testing::HasSubstr;
+using ::testing::Not;
+
+class SimpleFormatter : public CLI::FormatterBase {
+  public:
+    SimpleFormatter() : FormatterBase() {}
+
+    std::string make_help(const CLI::App *, std::string, CLI::AppFormatMode) const override {
+        return "This is really simple";
+    }
+};
+
+TEST(Formatter, Nothing) {
+    CLI::App app{"My prog"};
+
+    app.formatter(std::make_shared<SimpleFormatter>());
+
+    std::string help = app.help();
+
+    EXPECT_EQ(help, "This is really simple");
+}
+
+TEST(Formatter, NothingLambda) {
+    CLI::App app{"My prog"};
+
+    app.formatter_fn(
+        [](const CLI::App *, std::string, CLI::AppFormatMode) { return std::string("This is really simple"); });
+
+    std::string help = app.help();
+
+    EXPECT_EQ(help, "This is really simple");
+}
+
+TEST(Formatter, OptCustomize) {
+    CLI::App app{"My prog"};
+
+    auto optfmt = std::make_shared<CLI::Formatter>();
+    optfmt->column_width(25);
+    optfmt->label("REQUIRED", "(MUST HAVE)");
+    app.formatter(optfmt);
+
+    int v;
+    app.add_option("--opt", v, "Something")->required();
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("(MUST HAVE)"));
+    EXPECT_EQ(help,
+              "My prog\n"
+              "Usage: [OPTIONS]\n\n"
+              "Options:\n"
+              "  -h,--help              Print this help message and exit\n"
+              "  --opt INT (MUST HAVE)  Something\n");
+}
+
+TEST(Formatter, OptCustomizeSimple) {
+    CLI::App app{"My prog"};
+
+    app.get_formatter()->column_width(25);
+    app.get_formatter()->label("REQUIRED", "(MUST HAVE)");
+
+    int v;
+    app.add_option("--opt", v, "Something")->required();
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("(MUST HAVE)"));
+    EXPECT_EQ(help,
+              "My prog\n"
+              "Usage: [OPTIONS]\n\n"
+              "Options:\n"
+              "  -h,--help              Print this help message and exit\n"
+              "  --opt INT (MUST HAVE)  Something\n");
+}
+
+TEST(Formatter, AppCustomize) {
+    CLI::App app{"My prog"};
+    app.add_subcommand("subcom1", "This");
+
+    auto appfmt = std::make_shared<CLI::Formatter>();
+    appfmt->column_width(20);
+    appfmt->label("Usage", "Run");
+    app.formatter(appfmt);
+
+    app.add_subcommand("subcom2", "This");
+
+    std::string help = app.help();
+    EXPECT_EQ(help,
+              "My prog\n"
+              "Run: [OPTIONS] [SUBCOMMAND]\n\n"
+              "Options:\n"
+              "  -h,--help         Print this help message and exit\n\n"
+              "Subcommands:\n"
+              "  subcom1           This\n"
+              "  subcom2           This\n");
+}
+
+TEST(Formatter, AppCustomizeSimple) {
+    CLI::App app{"My prog"};
+    app.add_subcommand("subcom1", "This");
+
+    app.get_formatter()->column_width(20);
+    app.get_formatter()->label("Usage", "Run");
+
+    app.add_subcommand("subcom2", "This");
+
+    std::string help = app.help();
+    EXPECT_EQ(help,
+              "My prog\n"
+              "Run: [OPTIONS] [SUBCOMMAND]\n\n"
+              "Options:\n"
+              "  -h,--help         Print this help message and exit\n\n"
+              "Subcommands:\n"
+              "  subcom1           This\n"
+              "  subcom2           This\n");
+}
+
+TEST(Formatter, AllSub) {
+    CLI::App app{"My prog"};
+    CLI::App *sub = app.add_subcommand("subcom", "This");
+    sub->add_flag("--insub", "MyFlag");
+
+    std::string help = app.help("", CLI::AppFormatMode::All);
+    EXPECT_THAT(help, HasSubstr("--insub"));
+    EXPECT_THAT(help, HasSubstr("subcom"));
+}
diff --git a/packages/CLI11/tests/HelpTest.cpp b/packages/CLI11/tests/HelpTest.cpp
index 8ac69a832ef5b491eea436a2f3030268179b0d8c..17bee24c783d0d98ffa1cb128b0b70477c50355e 100644
--- a/packages/CLI11/tests/HelpTest.cpp
+++ b/packages/CLI11/tests/HelpTest.cpp
@@ -4,8 +4,8 @@
 #include "CLI/CLI.hpp"
 #endif
 
-#include "gtest/gtest.h"
 #include "gmock/gmock.h"
+#include "gtest/gtest.h"
 #include <fstream>
 
 using ::testing::HasSubstr;
@@ -36,7 +36,7 @@ TEST(THelp, Footer) {
 }
 
 TEST(THelp, OptionalPositional) {
-    CLI::App app{"My prog"};
+    CLI::App app{"My prog", "program"};
 
     std::string x;
     app.add_option("something", x, "My option here");
@@ -71,7 +71,7 @@ TEST(THelp, Hidden) {
 }
 
 TEST(THelp, OptionalPositionalAndOptions) {
-    CLI::App app{"My prog"};
+    CLI::App app{"My prog", "AnotherProgram"};
     app.add_flag("-q,--quick");
 
     std::string x;
@@ -82,7 +82,7 @@ TEST(THelp, OptionalPositionalAndOptions) {
     EXPECT_THAT(help, HasSubstr("My prog"));
     EXPECT_THAT(help, HasSubstr("-h,--help"));
     EXPECT_THAT(help, HasSubstr("Options:"));
-    EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] [something]"));
+    EXPECT_THAT(help, HasSubstr("Usage: AnotherProgram [OPTIONS] [something]"));
 }
 
 TEST(THelp, RequiredPositionalAndOptions) {
@@ -98,7 +98,7 @@ TEST(THelp, RequiredPositionalAndOptions) {
     EXPECT_THAT(help, HasSubstr("-h,--help"));
     EXPECT_THAT(help, HasSubstr("Options:"));
     EXPECT_THAT(help, HasSubstr("Positionals:"));
-    EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] something"));
+    EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS] something"));
 }
 
 TEST(THelp, MultiOpts) {
@@ -111,7 +111,7 @@ TEST(THelp, MultiOpts) {
 
     EXPECT_THAT(help, HasSubstr("My prog"));
     EXPECT_THAT(help, Not(HasSubstr("Positionals:")));
-    EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS]"));
+    EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS]"));
     EXPECT_THAT(help, HasSubstr("INT x 2"));
     EXPECT_THAT(help, HasSubstr("INT ..."));
 }
@@ -128,6 +128,7 @@ TEST(THelp, VectorOpts) {
 
 TEST(THelp, MultiPosOpts) {
     CLI::App app{"My prog"};
+    app.set_name("program");
     std::vector<int> x, y;
     app.add_option("quick", x, "Disc")->expected(2);
     app.add_option("vals", y, "Other");
@@ -161,7 +162,7 @@ TEST(THelp, Needs) {
 
     std::string help = app.help();
 
-    EXPECT_THAT(help, HasSubstr("Requires: --op1"));
+    EXPECT_THAT(help, HasSubstr("Needs: --op1"));
 }
 
 TEST(THelp, NeedsPositional) {
@@ -175,7 +176,7 @@ TEST(THelp, NeedsPositional) {
     std::string help = app.help();
 
     EXPECT_THAT(help, HasSubstr("Positionals:"));
-    EXPECT_THAT(help, HasSubstr("Requires: op1"));
+    EXPECT_THAT(help, HasSubstr("Needs: op1"));
 }
 
 TEST(THelp, Excludes) {
@@ -243,12 +244,12 @@ TEST(THelp, Subcom) {
     app.add_subcommand("sub2");
 
     std::string help = app.help();
-    EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] [SUBCOMMAND]"));
+    EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS] [SUBCOMMAND]"));
 
     app.require_subcommand();
 
     help = app.help();
-    EXPECT_THAT(help, HasSubstr("Usage: program [OPTIONS] SUBCOMMAND"));
+    EXPECT_THAT(help, HasSubstr("Usage: [OPTIONS] SUBCOMMAND"));
 
     help = sub1->help();
     EXPECT_THAT(help, HasSubstr("Usage: sub1"));
@@ -263,6 +264,17 @@ TEST(THelp, Subcom) {
     EXPECT_THAT(help, HasSubstr("Usage: ./myprogram sub2"));
 }
 
+TEST(THelp, MasterName) {
+    CLI::App app{"My prog", "MyRealName"};
+
+    char x[] = "./myprogram";
+
+    std::vector<char *> args = {x};
+    app.parse((int)args.size(), args.data());
+
+    EXPECT_THAT(app.help(), HasSubstr("Usage: MyRealName"));
+}
+
 TEST(THelp, IntDefaults) {
     CLI::App app{"My prog"};
 
@@ -305,6 +317,27 @@ TEST(THelp, OnlyOneHelp) {
     EXPECT_THROW(app.parse(input), CLI::ExtrasError);
 }
 
+TEST(THelp, OnlyOneAllHelp) {
+    CLI::App app{"My prog"};
+
+    // It is not supported to have more than one help flag, last one wins
+    app.set_help_all_flag("--help-all", "No short name allowed");
+    app.set_help_all_flag("--yelp", "Alias for help");
+
+    std::vector<std::string> input{"--help-all"};
+    EXPECT_THROW(app.parse(input), CLI::ExtrasError);
+
+    app.reset();
+    std::vector<std::string> input2{"--yelp"};
+    EXPECT_THROW(app.parse(input2), CLI::CallForAllHelp);
+
+    // Remove the flag
+    app.set_help_all_flag();
+    app.reset();
+    std::vector<std::string> input3{"--yelp"};
+    EXPECT_THROW(app.parse(input3), CLI::ExtrasError);
+}
+
 TEST(THelp, RemoveHelp) {
     CLI::App app{"My prog"};
     app.set_help_flag();
@@ -373,9 +406,9 @@ TEST(THelp, NiceName) {
     auto short_name = app.add_option("more,-x,-y", x);
     auto positional = app.add_option("posit", x);
 
-    EXPECT_EQ(long_name->single_name(), "--long");
-    EXPECT_EQ(short_name->single_name(), "-x");
-    EXPECT_EQ(positional->single_name(), "posit");
+    EXPECT_EQ(long_name->get_name(), "--long");
+    EXPECT_EQ(short_name->get_name(), "-x");
+    EXPECT_EQ(positional->get_name(), "posit");
 }
 
 TEST(Exit, ErrorWithHelp) {
@@ -389,6 +422,18 @@ TEST(Exit, ErrorWithHelp) {
     }
 }
 
+TEST(Exit, ErrorWithAllHelp) {
+    CLI::App app{"My prog"};
+    app.set_help_all_flag("--help-all", "All help");
+
+    std::vector<std::string> input{"--help-all"};
+    try {
+        app.parse(input);
+    } catch(const CLI::CallForAllHelp &e) {
+        EXPECT_EQ(static_cast<int>(CLI::ExitCodes::Success), e.get_exit_code());
+    }
+}
+
 TEST(Exit, ErrorWithoutHelp) {
     CLI::App app{"My prog"};
 
@@ -441,6 +486,44 @@ TEST_F(CapturedHelp, CallForHelp) {
     EXPECT_EQ(out.str(), app.help());
     EXPECT_EQ(err.str(), "");
 }
+TEST_F(CapturedHelp, CallForAllHelp) {
+    EXPECT_EQ(run(CLI::CallForAllHelp()), 0);
+    EXPECT_EQ(out.str(), app.help("", CLI::AppFormatMode::All));
+    EXPECT_EQ(err.str(), "");
+}
+TEST_F(CapturedHelp, CallForAllHelpOutput) {
+    app.set_help_all_flag("--help-all", "Help all");
+    app.add_subcommand("one", "One description");
+    CLI::App *sub = app.add_subcommand("two");
+    sub->add_flag("--three");
+
+    EXPECT_EQ(run(CLI::CallForAllHelp()), 0);
+    EXPECT_EQ(out.str(), app.help("", CLI::AppFormatMode::All));
+    EXPECT_EQ(err.str(), "");
+    EXPECT_THAT(out.str(), HasSubstr("one"));
+    EXPECT_THAT(out.str(), HasSubstr("two"));
+    EXPECT_THAT(out.str(), HasSubstr("--three"));
+
+    EXPECT_EQ(out.str(),
+              "My Test Program\n"
+              "Usage: [OPTIONS] [SUBCOMMAND]\n"
+              "\n"
+              "Options:\n"
+              "  -h,--help                   Print this help message and exit\n"
+              "  --help-all                  Help all\n"
+              "\n"
+              "Subcommands:\n"
+              "one -> One description\n"
+              "two\n"
+              "Options:\n"
+              "  --three                     \n");
+}
+TEST_F(CapturedHelp, NewFormattedHelp) {
+    app.formatter_fn([](const CLI::App *, std::string, CLI::AppFormatMode) { return "New Help"; });
+    EXPECT_EQ(run(CLI::CallForHelp()), 0);
+    EXPECT_EQ(out.str(), "New Help");
+    EXPECT_EQ(err.str(), "");
+}
 
 TEST_F(CapturedHelp, NormalError) {
     EXPECT_EQ(run(CLI::ExtrasError({"Thing"})), static_cast<int>(CLI::ExitCodes::ExtrasError));
@@ -477,3 +560,208 @@ TEST(THelp, CustomDoubleOption) {
 
     EXPECT_THAT(app.help(), Not(HasSubstr("x 2")));
 }
+
+TEST(THelp, AccessDescription) {
+    CLI::App app{"My description goes here"};
+
+    EXPECT_EQ(app.get_description(), "My description goes here");
+}
+
+TEST(THelp, CleanNeeds) {
+    CLI::App app;
+
+    int x;
+    auto a_name = app.add_option("-a,--alpha", x);
+    app.add_option("-b,--boo", x)->needs(a_name);
+
+    EXPECT_THAT(app.help(), Not(HasSubstr("Requires")));
+    EXPECT_THAT(app.help(), Not(HasSubstr("Needs: -a,--alpha")));
+    EXPECT_THAT(app.help(), HasSubstr("Needs: --alpha"));
+}
+
+TEST(THelp, RequiredPrintout) {
+    CLI::App app;
+
+    int x;
+    app.add_option("-a,--alpha", x)->required();
+
+    EXPECT_THAT(app.help(), HasSubstr(" REQUIRED"));
+}
+
+TEST(THelp, GroupOrder) {
+    CLI::App app;
+
+    app.add_flag("--one")->group("zee");
+    app.add_flag("--two")->group("aee");
+
+    std::string help = app.help();
+
+    auto zee_loc = help.find("zee");
+    auto aee_loc = help.find("aee");
+
+    EXPECT_NE(zee_loc, std::string::npos);
+    EXPECT_NE(aee_loc, std::string::npos);
+    EXPECT_LT(zee_loc, aee_loc);
+}
+
+TEST(THelp, ValidatorsText) {
+    CLI::App app;
+
+    std::string filename;
+    int x;
+    unsigned int y;
+    app.add_option("--f1", filename)->check(CLI::ExistingFile);
+    app.add_option("--f3", x)->check(CLI::Range(1, 4));
+    app.add_option("--f4", y)->check(CLI::Range(12));
+
+    std::string help = app.help();
+    EXPECT_THAT(help, HasSubstr("FILE"));
+    EXPECT_THAT(help, HasSubstr("INT in [1 - 4]"));
+    EXPECT_THAT(help, HasSubstr("INT in [0 - 12]")); // Loses UINT
+    EXPECT_THAT(help, Not(HasSubstr("TEXT")));
+}
+
+TEST(THelp, ValidatorsNonPathText) {
+    CLI::App app;
+
+    std::string filename;
+    app.add_option("--f2", filename)->check(CLI::NonexistentPath);
+
+    std::string help = app.help();
+    EXPECT_THAT(help, HasSubstr("PATH"));
+    EXPECT_THAT(help, Not(HasSubstr("TEXT")));
+}
+
+TEST(THelp, ValidatorsDirText) {
+    CLI::App app;
+
+    std::string filename;
+    app.add_option("--f2", filename)->check(CLI::ExistingDirectory);
+
+    std::string help = app.help();
+    EXPECT_THAT(help, HasSubstr("DIR"));
+    EXPECT_THAT(help, Not(HasSubstr("TEXT")));
+}
+
+TEST(THelp, ValidatorsPathText) {
+    CLI::App app;
+
+    std::string filename;
+    app.add_option("--f2", filename)->check(CLI::ExistingPath);
+
+    std::string help = app.help();
+    EXPECT_THAT(help, HasSubstr("PATH"));
+    EXPECT_THAT(help, Not(HasSubstr("TEXT")));
+}
+
+TEST(THelp, CombinedValidatorsText) {
+    CLI::App app;
+
+    std::string filename;
+    app.add_option("--f1", filename)->check(CLI::ExistingFile | CLI::ExistingDirectory);
+
+    // This would be nice if it put something other than string, but would it be path or file?
+    // Can't programatically tell!
+    // (Users can use ExistingPath, by the way)
+    std::string help = app.help();
+    EXPECT_THAT(help, HasSubstr("TEXT"));
+    EXPECT_THAT(help, Not(HasSubstr("PATH")));
+    EXPECT_THAT(help, Not(HasSubstr("FILE")));
+}
+
+// Don't do this in real life, please
+TEST(THelp, CombinedValidatorsPathyText) {
+    CLI::App app;
+
+    std::string filename;
+    app.add_option("--f1", filename)->check(CLI::ExistingPath | CLI::NonexistentPath);
+
+    // Combining validators with the same type string is OK
+    std::string help = app.help();
+    EXPECT_THAT(help, Not(HasSubstr("TEXT")));
+    EXPECT_THAT(help, HasSubstr("PATH"));
+}
+
+// #113 Part 2
+TEST(THelp, ChangingSet) {
+    CLI::App app;
+
+    std::set<int> vals{1, 2, 3};
+    int val;
+    app.add_set("--val", val, vals);
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("1"));
+    EXPECT_THAT(help, Not(HasSubstr("4")));
+
+    vals.insert(4);
+    vals.erase(1);
+
+    help = app.help();
+
+    EXPECT_THAT(help, Not(HasSubstr("1")));
+    EXPECT_THAT(help, HasSubstr("4"));
+}
+
+TEST(THelp, ChangingSetDefaulted) {
+    CLI::App app;
+
+    std::set<int> vals{1, 2, 3};
+    int val = 2;
+    app.add_set("--val", val, vals, "", true);
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("1"));
+    EXPECT_THAT(help, Not(HasSubstr("4")));
+
+    vals.insert(4);
+    vals.erase(1);
+
+    help = app.help();
+
+    EXPECT_THAT(help, Not(HasSubstr("1")));
+    EXPECT_THAT(help, HasSubstr("4"));
+}
+TEST(THelp, ChangingCaselessSet) {
+    CLI::App app;
+
+    std::set<std::string> vals{"1", "2", "3"};
+    std::string val;
+    app.add_set_ignore_case("--val", val, vals);
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("1"));
+    EXPECT_THAT(help, Not(HasSubstr("4")));
+
+    vals.insert("4");
+    vals.erase("1");
+
+    help = app.help();
+
+    EXPECT_THAT(help, Not(HasSubstr("1")));
+    EXPECT_THAT(help, HasSubstr("4"));
+}
+
+TEST(THelp, ChangingCaselessSetDefaulted) {
+    CLI::App app;
+
+    std::set<std::string> vals{"1", "2", "3"};
+    std::string val = "2";
+    app.add_set_ignore_case("--val", val, vals, "", true);
+
+    std::string help = app.help();
+
+    EXPECT_THAT(help, HasSubstr("1"));
+    EXPECT_THAT(help, Not(HasSubstr("4")));
+
+    vals.insert("4");
+    vals.erase("1");
+
+    help = app.help();
+
+    EXPECT_THAT(help, Not(HasSubstr("1")));
+    EXPECT_THAT(help, HasSubstr("4"));
+}
diff --git a/packages/CLI11/tests/HelpersTest.cpp b/packages/CLI11/tests/HelpersTest.cpp
index 281b8f6df2ffecd5e20d88df7f6fe0b1bc0e4b15..d79354e78b131c10d6e35cfd0eb08733127676c3 100644
--- a/packages/CLI11/tests/HelpersTest.cpp
+++ b/packages/CLI11/tests/HelpersTest.cpp
@@ -1,10 +1,10 @@
 #include "app_helper.hpp"
 
+#include <complex>
+#include <cstdint>
 #include <cstdio>
 #include <fstream>
-#include <cstdint>
 #include <string>
-#include <complex>
 
 TEST(Split, SimpleByToken) {
     auto out = CLI::detail::split("one.two.three", '.');
@@ -155,6 +155,63 @@ TEST(Validators, PathNotExistsDir) {
     EXPECT_NE(CLI::ExistingPath(mydir), "");
 }
 
+TEST(Validators, CombinedAndRange) {
+    auto crange = CLI::Range(0, 12) & CLI::Range(4, 16);
+    EXPECT_TRUE(crange("4").empty());
+    EXPECT_TRUE(crange("12").empty());
+    EXPECT_TRUE(crange("7").empty());
+
+    EXPECT_FALSE(crange("-2").empty());
+    EXPECT_FALSE(crange("2").empty());
+    EXPECT_FALSE(crange("15").empty());
+    EXPECT_FALSE(crange("16").empty());
+    EXPECT_FALSE(crange("18").empty());
+}
+
+TEST(Validators, CombinedOrRange) {
+    auto crange = CLI::Range(0, 4) | CLI::Range(8, 12);
+
+    EXPECT_FALSE(crange("-2").empty());
+    EXPECT_TRUE(crange("2").empty());
+    EXPECT_FALSE(crange("5").empty());
+    EXPECT_TRUE(crange("8").empty());
+    EXPECT_TRUE(crange("12").empty());
+    EXPECT_FALSE(crange("16").empty());
+}
+
+TEST(Validators, CombinedPaths) {
+    std::string myfile{"TestFileNotUsed.txt"};
+    EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
+    bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
+    EXPECT_TRUE(ok);
+
+    std::string dir{"../tests"};
+    std::string notpath{"nondirectory"};
+
+    auto path_or_dir = CLI::ExistingPath | CLI::ExistingDirectory;
+    EXPECT_TRUE(path_or_dir(dir).empty());
+    EXPECT_TRUE(path_or_dir(myfile).empty());
+    EXPECT_FALSE(path_or_dir(notpath).empty());
+
+    auto file_or_dir = CLI::ExistingFile | CLI::ExistingDirectory;
+    EXPECT_TRUE(file_or_dir(dir).empty());
+    EXPECT_TRUE(file_or_dir(myfile).empty());
+    EXPECT_FALSE(file_or_dir(notpath).empty());
+
+    auto path_and_dir = CLI::ExistingPath & CLI::ExistingDirectory;
+    EXPECT_TRUE(path_and_dir(dir).empty());
+    EXPECT_FALSE(path_and_dir(myfile).empty());
+    EXPECT_FALSE(path_and_dir(notpath).empty());
+
+    auto path_and_file = CLI::ExistingFile & CLI::ExistingDirectory;
+    EXPECT_FALSE(path_and_file(dir).empty());
+    EXPECT_FALSE(path_and_file(myfile).empty());
+    EXPECT_FALSE(path_and_file(notpath).empty());
+
+    std::remove(myfile.c_str());
+    EXPECT_FALSE(CLI::ExistingFile(myfile).empty());
+}
+
 // Yes, this is testing an app_helper :)
 TEST(AppHelper, TempfileCreated) {
     std::string name = "TestFileNotUsed.txt";
diff --git a/packages/CLI11/tests/IniTest.cpp b/packages/CLI11/tests/IniTest.cpp
index f2714506b01b29d9383849b9b804d00aaf2c2c3e..92c4e35e58bbaf4d1122d95054bb9f251eaea91f 100644
--- a/packages/CLI11/tests/IniTest.cpp
+++ b/packages/CLI11/tests/IniTest.cpp
@@ -1,8 +1,8 @@
 #include "app_helper.hpp"
 
+#include "gmock/gmock.h"
 #include <cstdio>
 #include <sstream>
-#include "gmock/gmock.h"
 
 using ::testing::HasSubstr;
 using ::testing::Not;
@@ -15,13 +15,13 @@ TEST(StringBased, First) {
 
     ofile.seekg(0, std::ios::beg);
 
-    std::vector<CLI::detail::ini_ret_t> output = CLI::detail::parse_ini(ofile);
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
 
     EXPECT_EQ((size_t)2, output.size());
-    EXPECT_EQ("one", output.at(0).name());
+    EXPECT_EQ("one", output.at(0).name);
     EXPECT_EQ((size_t)1, output.at(0).inputs.size());
     EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name());
+    EXPECT_EQ("two", output.at(1).name);
     EXPECT_EQ((size_t)1, output.at(1).inputs.size());
     EXPECT_EQ("four", output.at(1).inputs.at(0));
 }
@@ -36,13 +36,13 @@ TEST(StringBased, FirstWithComments) {
 
     ofile.seekg(0, std::ios::beg);
 
-    auto output = CLI::detail::parse_ini(ofile);
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
 
     EXPECT_EQ((size_t)2, output.size());
-    EXPECT_EQ("one", output.at(0).name());
+    EXPECT_EQ("one", output.at(0).name);
     EXPECT_EQ((size_t)1, output.at(0).inputs.size());
     EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name());
+    EXPECT_EQ("two", output.at(1).name);
     EXPECT_EQ((size_t)1, output.at(1).inputs.size());
     EXPECT_EQ("four", output.at(1).inputs.at(0));
 }
@@ -56,16 +56,16 @@ TEST(StringBased, Quotes) {
 
     ofile.seekg(0, std::ios::beg);
 
-    auto output = CLI::detail::parse_ini(ofile);
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
 
     EXPECT_EQ((size_t)3, output.size());
-    EXPECT_EQ("one", output.at(0).name());
+    EXPECT_EQ("one", output.at(0).name);
     EXPECT_EQ((size_t)1, output.at(0).inputs.size());
     EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name());
+    EXPECT_EQ("two", output.at(1).name);
     EXPECT_EQ((size_t)1, output.at(1).inputs.size());
     EXPECT_EQ("four", output.at(1).inputs.at(0));
-    EXPECT_EQ("five", output.at(2).name());
+    EXPECT_EQ("five", output.at(2).name);
     EXPECT_EQ((size_t)1, output.at(2).inputs.size());
     EXPECT_EQ("six and seven", output.at(2).inputs.at(0));
 }
@@ -79,16 +79,16 @@ TEST(StringBased, Vector) {
 
     ofile.seekg(0, std::ios::beg);
 
-    auto output = CLI::detail::parse_ini(ofile);
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
 
     EXPECT_EQ((size_t)3, output.size());
-    EXPECT_EQ("one", output.at(0).name());
+    EXPECT_EQ("one", output.at(0).name);
     EXPECT_EQ((size_t)1, output.at(0).inputs.size());
     EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name());
+    EXPECT_EQ("two", output.at(1).name);
     EXPECT_EQ((size_t)1, output.at(1).inputs.size());
     EXPECT_EQ("four", output.at(1).inputs.at(0));
-    EXPECT_EQ("five", output.at(2).name());
+    EXPECT_EQ("five", output.at(2).name);
     EXPECT_EQ((size_t)3, output.at(2).inputs.size());
     EXPECT_EQ("six", output.at(2).inputs.at(0));
     EXPECT_EQ("and", output.at(2).inputs.at(1));
@@ -103,13 +103,13 @@ TEST(StringBased, Spaces) {
 
     ofile.seekg(0, std::ios::beg);
 
-    auto output = CLI::detail::parse_ini(ofile);
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
 
     EXPECT_EQ((size_t)2, output.size());
-    EXPECT_EQ("one", output.at(0).name());
+    EXPECT_EQ("one", output.at(0).name);
     EXPECT_EQ((size_t)1, output.at(0).inputs.size());
     EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name());
+    EXPECT_EQ("two", output.at(1).name);
     EXPECT_EQ((size_t)1, output.at(1).inputs.size());
     EXPECT_EQ("four", output.at(1).inputs.at(0));
 }
@@ -123,16 +123,17 @@ TEST(StringBased, Sections) {
 
     ofile.seekg(0, std::ios::beg);
 
-    auto output = CLI::detail::parse_ini(ofile);
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
 
     EXPECT_EQ((size_t)2, output.size());
-    EXPECT_EQ("one", output.at(0).name());
+    EXPECT_EQ("one", output.at(0).name);
     EXPECT_EQ((size_t)1, output.at(0).inputs.size());
     EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name());
-    EXPECT_EQ("second", output.at(1).parent());
+    EXPECT_EQ("two", output.at(1).name);
+    EXPECT_EQ("second", output.at(1).parents.at(0));
     EXPECT_EQ((size_t)1, output.at(1).inputs.size());
     EXPECT_EQ("four", output.at(1).inputs.at(0));
+    EXPECT_EQ("second.two", output.at(1).fullname());
 }
 
 TEST(StringBased, SpacesSections) {
@@ -146,14 +147,15 @@ TEST(StringBased, SpacesSections) {
 
     ofile.seekg(0, std::ios::beg);
 
-    auto output = CLI::detail::parse_ini(ofile);
+    std::vector<CLI::ConfigItem> output = CLI::ConfigINI().from_config(ofile);
 
     EXPECT_EQ((size_t)2, output.size());
-    EXPECT_EQ("one", output.at(0).name());
+    EXPECT_EQ("one", output.at(0).name);
     EXPECT_EQ((size_t)1, output.at(0).inputs.size());
     EXPECT_EQ("three", output.at(0).inputs.at(0));
-    EXPECT_EQ("two", output.at(1).name());
-    EXPECT_EQ("second", output.at(1).parent());
+    EXPECT_EQ("two", output.at(1).name);
+    EXPECT_EQ((size_t)1, output.at(1).parents.size());
+    EXPECT_EQ("second", output.at(1).parents.at(0));
     EXPECT_EQ((size_t)1, output.at(1).inputs.size());
     EXPECT_EQ("four", output.at(1).inputs.at(0));
 }
@@ -199,7 +201,7 @@ TEST_F(TApp, IniSuccessOnUnknownOption) {
     TempFile tmpini{"TestIniTmp.ini"};
 
     app.set_config("--config", tmpini);
-    app.allow_ini_extras(true);
+    app.allow_config_extras(true);
 
     {
         std::ofstream out{tmpini};
@@ -209,7 +211,7 @@ TEST_F(TApp, IniSuccessOnUnknownOption) {
 
     int two = 0;
     app.add_option("--two", two);
-    EXPECT_NO_THROW(run());
+    run();
     EXPECT_EQ(99, two);
 }
 
@@ -217,7 +219,7 @@ TEST_F(TApp, IniGetRemainingOption) {
     TempFile tmpini{"TestIniTmp.ini"};
 
     app.set_config("--config", tmpini);
-    app.allow_ini_extras(true);
+    app.allow_config_extras(true);
 
     std::string ExtraOption = "three";
     std::string ExtraOptionValue = "3";
@@ -238,7 +240,7 @@ TEST_F(TApp, IniGetNoRemaining) {
     TempFile tmpini{"TestIniTmp.ini"};
 
     app.set_config("--config", tmpini);
-    app.allow_ini_extras(true);
+    app.allow_config_extras(true);
 
     {
         std::ofstream out{tmpini};
@@ -413,7 +415,7 @@ TEST_F(TApp, IniLayered) {
     auto subsubcom = subcom->add_subcommand("subsubcom");
     subsubcom->add_option("--val", three);
 
-    ASSERT_NO_THROW(run());
+    run();
 
     EXPECT_EQ(1, one);
     EXPECT_EQ(2, two);
@@ -432,7 +434,7 @@ TEST_F(TApp, IniFailure) {
         out << "val=1" << std::endl;
     }
 
-    EXPECT_THROW(run(), CLI::INIError);
+    EXPECT_THROW(run(), CLI::ConfigError);
 }
 
 TEST_F(TApp, IniConfigurable) {
@@ -467,7 +469,7 @@ TEST_F(TApp, IniNotConfigurable) {
         out << "val=1" << std::endl;
     }
 
-    EXPECT_THROW(run(), CLI::INIError);
+    EXPECT_THROW(run(), CLI::ConfigError);
 }
 
 TEST_F(TApp, IniSubFailure) {
@@ -483,7 +485,7 @@ TEST_F(TApp, IniSubFailure) {
         out << "val=1" << std::endl;
     }
 
-    EXPECT_THROW(run(), CLI::INIError);
+    EXPECT_THROW(run(), CLI::ConfigError);
 }
 
 TEST_F(TApp, IniNoSubFailure) {
@@ -498,7 +500,7 @@ TEST_F(TApp, IniNoSubFailure) {
         out << "val=1" << std::endl;
     }
 
-    EXPECT_THROW(run(), CLI::INIError);
+    EXPECT_THROW(run(), CLI::ConfigError);
 }
 
 TEST_F(TApp, IniFlagConvertFailure) {
@@ -638,7 +640,7 @@ TEST_F(TApp, IniOutputShortSingleDescription) {
 
     run();
 
-    std::string str = app.config_to_str(true, "", true);
+    std::string str = app.config_to_str(true, true);
     EXPECT_THAT(str, HasSubstr("; " + description + "\n" + flag + "=false\n"));
 }
 
@@ -652,7 +654,7 @@ TEST_F(TApp, IniOutputShortDoubleDescription) {
 
     run();
 
-    std::string str = app.config_to_str(true, "", true);
+    std::string str = app.config_to_str(true, true);
     EXPECT_EQ(str, "; " + description1 + "\n" + flag1 + "=false\n\n; " + description2 + "\n" + flag2 + "=false\n");
 }
 
@@ -663,7 +665,7 @@ TEST_F(TApp, IniOutputMultiLineDescription) {
 
     run();
 
-    std::string str = app.config_to_str(true, "", true);
+    std::string str = app.config_to_str(true, true);
     EXPECT_THAT(str, HasSubstr("; Some short description.\n"));
     EXPECT_THAT(str, HasSubstr("; That has lines.\n"));
     EXPECT_THAT(str, HasSubstr(flag + "=false\n"));
diff --git a/packages/CLI11/tests/OptionalTest.cpp b/packages/CLI11/tests/OptionalTest.cpp
index 305f42746ae4c900a86e6274782b6190f75f6f4f..6c519e1f261a1254418e0a2f7e5f0f3216671267 100644
--- a/packages/CLI11/tests/OptionalTest.cpp
+++ b/packages/CLI11/tests/OptionalTest.cpp
@@ -3,10 +3,10 @@
 
 #include "app_helper.hpp"
 
-#ifdef CLI11_OPTIONAL
+#if CLI11_STD_OPTIONAL
 
-TEST_F(TApp, OptionalTest) {
-    CLI::optional<int> opt;
+TEST_F(TApp, StdOptionalTest) {
+    std::optional<int> opt;
     app.add_option("-c,--count", opt);
     run();
     EXPECT_FALSE(opt);
@@ -24,8 +24,52 @@ TEST_F(TApp, OptionalTest) {
     EXPECT_EQ(*opt, 3);
 }
 
-#else
+#endif
+#if CLI11_EXPERIMENTAL_OPTIONAL
 
-TEST_F(TApp, DISABLED_OptionalTest) {}
+TEST_F(TApp, ExperimentalOptionalTest) {
+    std::experimental::optional<int> opt;
+    app.add_option("-c,--count", opt);
+    run();
+    EXPECT_FALSE(opt);
+
+    app.reset();
+    args = {"-c", "1"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, 1);
+
+    app.reset();
+    args = {"--count", "3"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, 3);
+}
+
+#endif
+#if CLI11_BOOST_OPTIONAL
 
+TEST_F(TApp, BoostOptionalTest) {
+    boost::optional<int> opt;
+    app.add_option("-c,--count", opt);
+    run();
+    EXPECT_FALSE(opt);
+
+    app.reset();
+    args = {"-c", "1"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, 1);
+
+    app.reset();
+    args = {"--count", "3"};
+    run();
+    EXPECT_TRUE(opt);
+    EXPECT_EQ(*opt, 3);
+}
+
+#endif
+
+#if !CLI11_OPTIONAL
+TEST_F(TApp, DISABLED_OptionalTest) {}
 #endif
diff --git a/packages/CLI11/tests/SubcommandTest.cpp b/packages/CLI11/tests/SubcommandTest.cpp
index 84fa0ad2d995047b3f4bbbbb277abe6cf3674940..1a8174d942ae75cd5a9cbbf05e609c1c8610f337 100644
--- a/packages/CLI11/tests/SubcommandTest.cpp
+++ b/packages/CLI11/tests/SubcommandTest.cpp
@@ -1,7 +1,7 @@
 #include "app_helper.hpp"
 
-#include "gtest/gtest.h"
 #include "gmock/gmock.h"
+#include "gtest/gtest.h"
 
 using ::testing::HasSubstr;
 using ::testing::Not;
@@ -448,6 +448,13 @@ TEST_F(TApp, PrefixSubcom) {
     EXPECT_EQ(subc->remaining(), std::vector<std::string>({"other", "--simple", "--mine"}));
 }
 
+TEST_F(TApp, InheritHelpAllFlag) {
+    app.set_help_all_flag("--help-all");
+    auto subc = app.add_subcommand("subc");
+    auto help_opt_list = subc->get_options([](const CLI::Option *opt) { return opt->get_name() == "--help-all"; });
+    EXPECT_EQ(help_opt_list.size(), (size_t)1);
+}
+
 struct SubcommandProgram : public TApp {
 
     CLI::App *start;
@@ -536,6 +543,8 @@ TEST_F(TApp, SubcomInheritCaseCheck) {
 
     run();
     EXPECT_EQ((size_t)0, app.get_subcommands().size());
+    EXPECT_EQ((size_t)2, app.get_subcommands({}).size());
+    EXPECT_EQ((size_t)1, app.get_subcommands([](const CLI::App *s) { return s->get_name() == "sub1"; }).size());
 
     app.reset();
     args = {"SuB1"};
diff --git a/packages/CLI11/tests/TimerTest.cpp b/packages/CLI11/tests/TimerTest.cpp
index 8e03088d7582e5c8c6611f64d30e9f2b38cfbb5d..8051c9ec6b6f51d27564f26ba856ab430ce02ca8 100644
--- a/packages/CLI11/tests/TimerTest.cpp
+++ b/packages/CLI11/tests/TimerTest.cpp
@@ -1,10 +1,10 @@
-#include "gtest/gtest.h"
-#include "gmock/gmock.h"
 #include "CLI/Timer.hpp"
-#include <string>
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
 #include <chrono>
-#include <thread>
 #include <sstream>
+#include <string>
+#include <thread>
 
 using ::testing::HasSubstr;
 
diff --git a/packages/CLI11/tests/app_helper.hpp b/packages/CLI11/tests/app_helper.hpp
index 4280cc9e802277a6bd18652b07d675a5c5b22652..e2ae3aba04029d83d2c48b4d836fa63dbea9adcd 100644
--- a/packages/CLI11/tests/app_helper.hpp
+++ b/packages/CLI11/tests/app_helper.hpp
@@ -26,7 +26,7 @@ class TempFile {
     std::string _name;
 
   public:
-    TempFile(std::string name) : _name(name) {
+    explicit TempFile(std::string name) : _name(name) {
         if(!CLI::NonexistentPath(_name).empty())
             throw std::runtime_error(_name);
     }
diff --git a/packages/CLI11/tests/informational.cpp b/packages/CLI11/tests/informational.cpp
index 89c73ff9aa3040e59a1c9735719c526edaf441ba..b5a0098c7566e6813d667e9917c69a625d737f55 100644
--- a/packages/CLI11/tests/informational.cpp
+++ b/packages/CLI11/tests/informational.cpp
@@ -28,21 +28,21 @@ int main() {
     std::cout << "no\n";
 #endif
 
-#ifdef CLI11_OPTIONAL
+#if CLI11_OPTIONAL
     std::cout << "  [Available as CLI::optional]";
 #else
     std::cout << "  No optional library found\n";
 #endif
 
-#ifdef CLI11_STD_OPTIONAL
+#if CLI11_STD_OPTIONAL
     std::cout << "  std::optional support active\n";
 #endif
 
-#ifdef CLI11_EXPERIMENTAL_OPTIONAL
+#if CLI11_EXPERIMENTAL_OPTIONAL
     std::cout << "  std::experimental::optional support active\n";
 #endif
 
-#ifdef CLI11_BOOST_OPTIONAL
+#if CLI11_BOOST_OPTIONAL
     std::cout << "  boost::optional support active\n";
 #endif