mirror of
https://github.com/CLIUtils/CLI11.git
synced 2025-04-29 20:23:55 +00:00
feat: unicode support (#804)
* Add unicode support tests * Add unicode parse tests * Implement #14 * Slim down Windows.h * Fix documentation comments * Fix clang-tidy and cpplint * Update README * Fix clang-tidy * Fix to_path not being available on linux * Add roundtrip encoding tests * style: pre-commit.ci fixes * Fix pre-commit.ci * Fix codacy * Exclude parse_unicode which should not contain a newline from pre-commit * Remove a test which breaks CI * Fix build in CI * Replace broken execute_with tests * Fix wide string conversions on all systems * Fix system args on apple * style: pre-commit.ci fixes * Fix some includes * Fix wrong size calculation and comments * Add guards around codecvt * Fix _Pragma not recognized on MSVC * Fix bad macro check * Fix include * Fix narrow and widen when codecvt is missing * Fix some weird bug in old MSVC * Add dependent applications to meson-build * Fix precompilation * Fix lint * Fix coverage * Update README * style: pre-commit.ci fixes * Fix lint * Fix coverage * Fix optional braces offending clang * Remove copied comments from Windows.h * Suppress flawfinder detects * Fix cmake config tests failing because of a missing lib * chore: update copyright on new files to 2023 Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> * style: pre-commit.ci fixes Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
This commit is contained in:
parent
48624ead3c
commit
a227cd10fc
18
.github/workflows/tests.yml
vendored
18
.github/workflows/tests.yml
vendored
@ -128,9 +128,9 @@ jobs:
|
|||||||
- name: Build
|
- name: Build
|
||||||
run: meson compile -C build-meson
|
run: meson compile -C build-meson
|
||||||
|
|
||||||
cmake-config:
|
cmake-config-ubuntu-1804:
|
||||||
name: CMake config check
|
name: CMake config check (Ubuntu 18.04)
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
@ -175,6 +175,12 @@ jobs:
|
|||||||
cmake-version: "3.10"
|
cmake-version: "3.10"
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
|
|
||||||
|
cmake-config-ubuntu-2004:
|
||||||
|
name: CMake config check (Ubuntu 20.04)
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Check CMake 3.11 (full)
|
- name: Check CMake 3.11 (full)
|
||||||
uses: ./.github/actions/quick_cmake
|
uses: ./.github/actions/quick_cmake
|
||||||
with:
|
with:
|
||||||
@ -212,6 +218,12 @@ jobs:
|
|||||||
cmake-version: "3.16"
|
cmake-version: "3.16"
|
||||||
if: success() || failure()
|
if: success() || failure()
|
||||||
|
|
||||||
|
cmake-config-ubuntu-2204:
|
||||||
|
name: CMake config check (Ubuntu 22.04)
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Check CMake 3.17
|
- name: Check CMake 3.17
|
||||||
uses: ./.github/actions/quick_cmake
|
uses: ./.github/actions/quick_cmake
|
||||||
with:
|
with:
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,8 +6,10 @@ a.out*
|
|||||||
/CMakeFiles/*
|
/CMakeFiles/*
|
||||||
/cmake_install.cmake
|
/cmake_install.cmake
|
||||||
/*.kdev4
|
/*.kdev4
|
||||||
|
/.vscode
|
||||||
/html/*
|
/html/*
|
||||||
!/meson.build
|
!/meson.build
|
||||||
|
/CMakeUserPresets.json
|
||||||
|
|
||||||
/node_modules/*
|
/node_modules/*
|
||||||
/package.json
|
/package.json
|
||||||
|
14
CLI11.hpp.in
14
CLI11.hpp.in
@ -40,10 +40,24 @@
|
|||||||
|
|
||||||
{macros_hpp}
|
{macros_hpp}
|
||||||
|
|
||||||
|
{slim_windows_h_hpp}
|
||||||
|
|
||||||
{validators_hpp_filesystem}
|
{validators_hpp_filesystem}
|
||||||
|
|
||||||
|
{encoding_includes}
|
||||||
|
|
||||||
|
{argv_inl_includes}
|
||||||
|
|
||||||
namespace {namespace} {{
|
namespace {namespace} {{
|
||||||
|
|
||||||
|
{encoding_hpp}
|
||||||
|
|
||||||
|
{encoding_inl_hpp}
|
||||||
|
|
||||||
|
{argv_hpp}
|
||||||
|
|
||||||
|
{argv_inl_hpp}
|
||||||
|
|
||||||
{string_tools_hpp}
|
{string_tools_hpp}
|
||||||
|
|
||||||
{string_tools_inl_hpp}
|
{string_tools_inl_hpp}
|
||||||
|
@ -4,6 +4,7 @@ linelength=120 # As in .clang-format
|
|||||||
# Unused filters
|
# Unused filters
|
||||||
filter=-build/c++11 # Reports e.g. chrono and thread, which overlap with Chromium's API. Not applicable to general C++ projects.
|
filter=-build/c++11 # Reports e.g. chrono and thread, which overlap with Chromium's API. Not applicable to general C++ projects.
|
||||||
filter=-build/include_order # Requires unusual include order that encourages creating not self-contained headers
|
filter=-build/include_order # Requires unusual include order that encourages creating not self-contained headers
|
||||||
|
filter=-build/include_subdir # Prevents including files in current directory for whatever reason
|
||||||
filter=-readability/nolint # Conflicts with clang-tidy
|
filter=-readability/nolint # Conflicts with clang-tidy
|
||||||
filter=-readability/check # Catch uses CHECK(a == b) (Tests only)
|
filter=-readability/check # Catch uses CHECK(a == b) (Tests only)
|
||||||
filter=-build/namespaces # Currently using it for one test (Tests only)
|
filter=-build/namespaces # Currently using it for one test (Tests only)
|
||||||
|
119
README.md
119
README.md
@ -50,6 +50,7 @@ set with a simple and intuitive interface.
|
|||||||
- [Formatting](#formatting)
|
- [Formatting](#formatting)
|
||||||
- [Subclassing](#subclassing)
|
- [Subclassing](#subclassing)
|
||||||
- [How it works](#how-it-works)
|
- [How it works](#how-it-works)
|
||||||
|
- [Unicode support](#unicode-support)
|
||||||
- [Utilities](#utilities)
|
- [Utilities](#utilities)
|
||||||
- [Other libraries](#other-libraries)
|
- [Other libraries](#other-libraries)
|
||||||
- [API](#api)
|
- [API](#api)
|
||||||
@ -164,9 +165,6 @@ this library:
|
|||||||
option to disable it).
|
option to disable it).
|
||||||
- Autocomplete: This might eventually be added to both Plumbum and CLI11, but it
|
- Autocomplete: This might eventually be added to both Plumbum and CLI11, but it
|
||||||
is not supported yet.
|
is not supported yet.
|
||||||
- Wide strings / unicode: Since this uses the standard library only, it might be
|
|
||||||
hard to properly implement, but I would be open to suggestions in how to do
|
|
||||||
this.
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@ -278,13 +276,13 @@ To set up, add options, and run, your main function will look something like
|
|||||||
this:
|
this:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
int main(int argc, char** argv) {
|
int main() {
|
||||||
CLI::App app{"App description"};
|
CLI::App app{"App description"};
|
||||||
|
|
||||||
std::string filename = "default";
|
std::string filename = "default";
|
||||||
app.add_option("-f,--file", filename, "A help string");
|
app.add_option("-f,--file", filename, "A help string");
|
||||||
|
|
||||||
CLI11_PARSE(app, argc, argv);
|
CLI11_PARSE(app);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -293,7 +291,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
try {
|
try {
|
||||||
app.parse(argc, argv);
|
app.parse();
|
||||||
} catch (const CLI::ParseError &e) {
|
} catch (const CLI::ParseError &e) {
|
||||||
return app.exit(e);
|
return app.exit(e);
|
||||||
}
|
}
|
||||||
@ -306,6 +304,25 @@ inside the catch block; for example, help flags intentionally short-circuit all
|
|||||||
other processing for speed and to ensure required options and the like do not
|
other processing for speed and to ensure required options and the like do not
|
||||||
interfere.
|
interfere.
|
||||||
|
|
||||||
|
</p></details>
|
||||||
|
|
||||||
|
<details><summary>Note: Why are argc and argv not used? (click to expand)</summary><p>
|
||||||
|
|
||||||
|
`argc` and `argv` may contain incorrect information on Windows when unicode text
|
||||||
|
is passed in. Check out a section on [unicode support](#unicode-support) below.
|
||||||
|
|
||||||
|
If this is not a concern, you can explicitly pass `argc` and `argv` from main or
|
||||||
|
from an external preprocessor of CLI arguments to `parse`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
// ...
|
||||||
|
|
||||||
|
CLI11_PARSE(app, argc, argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
</p></details>
|
</p></details>
|
||||||
</br>
|
</br>
|
||||||
|
|
||||||
@ -1468,6 +1485,96 @@ app.add_option("--fancy-count", [](std::vector<std::string> val){
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Unicode support
|
||||||
|
|
||||||
|
CLI11 supports Unicode and wide strings as defined in the
|
||||||
|
[UTF-8 Everywhere](http://utf8everywhere.org/) manifesto. In particular:
|
||||||
|
|
||||||
|
- The library can parse a wide version of command-line arguments on Windows,
|
||||||
|
which are converted internally to UTF-8 (more on this below);
|
||||||
|
- You can store option values in `std::wstring`, in which case they will be
|
||||||
|
converted to a correct wide string encoding on your system (UTF-16 on Windows
|
||||||
|
and UTF-32 on most other systems);
|
||||||
|
- Instead of storing wide strings, it is recommended to use provided `widen` and
|
||||||
|
`narrow` functions to convert to and from wide strings when actually necessary
|
||||||
|
(such as when calling into Windows APIs).
|
||||||
|
|
||||||
|
When using the command line on Windows with unicode arguments, your `main`
|
||||||
|
function may already receive broken Unicode. Parsing `argv` at that point will
|
||||||
|
not give you a correct string. To fix this, you have three options:
|
||||||
|
|
||||||
|
1. If you pass unmodified command-line arguments to CLI11, call `app.parse()`
|
||||||
|
instead of `app.parse(argc, argv)` (or `CLI11_PARSE(app)` instead of
|
||||||
|
`CLI11_PARSE(app, argc, argv)`). The library will find correct arguments
|
||||||
|
itself.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int main() {
|
||||||
|
CLI::App app;
|
||||||
|
// ...
|
||||||
|
CLI11_PARSE(app);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Get correct arguments with which the program was originally executed using
|
||||||
|
provided functions: `CLI::argc()` and `CLI::argv()`. These two methods are
|
||||||
|
the only cross-platform ways of handling unicode correctly.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int main() {
|
||||||
|
CLI::App app;
|
||||||
|
// ...
|
||||||
|
CLI11_PARSE(app, CLI::argc(), CLI::argv());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Use the Windows-only non-standard `wmain` function, which accepts
|
||||||
|
`wchar_t *argv[]` instead of `char* argv[]`. Parsing this will allow CLI to
|
||||||
|
convert wide strings to UTF-8 without losing information.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int wmain(int argc, wchar_t *argv[]) {
|
||||||
|
CLI::App app;
|
||||||
|
// ...
|
||||||
|
CLI11_PARSE(app, argc, argv);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Retrieve arguments yourself by using Windows APIs like
|
||||||
|
[`CommandLineToArgvW`](https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw)
|
||||||
|
and pass them to CLI. This is what the library is doing under the hood in
|
||||||
|
`CLI::argv()`.
|
||||||
|
|
||||||
|
The library provides functions to convert between UTF-8 and wide strings:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
namespace CLI {
|
||||||
|
std::string narrow(const std::wstring &str);
|
||||||
|
std::string narrow(const wchar_t *str);
|
||||||
|
std::string narrow(const wchar_t *str, std::size_t size);
|
||||||
|
std::string narrow(std::wstring_view str); // C++17
|
||||||
|
|
||||||
|
std::wstring widen(const std::string &str);
|
||||||
|
std::wstring widen(const char *str);
|
||||||
|
std::wstring widen(const char *str, std::size_t size);
|
||||||
|
std::wstring widen(std::string_view str); // C++17
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Note on using Unicode paths
|
||||||
|
|
||||||
|
When creating a `filesystem::path` from a UTF-8 path on Windows, you need to
|
||||||
|
convert it to a wide string first. CLI11 provides a platform-independent
|
||||||
|
`to_path` function, which will convert a UTF-8 string to path, the right way:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string utf8_name = "Hello Halló Привет 你好 👩🚀❤️.txt";
|
||||||
|
|
||||||
|
std::filesystem::path p = CLI::to_path(utf8_name);
|
||||||
|
std::ifstream stream(CLI::to_path(utf8_name));
|
||||||
|
// etc.
|
||||||
|
```
|
||||||
|
|
||||||
### Utilities
|
### Utilities
|
||||||
|
|
||||||
There are a few other utilities that are often useful in CLI programming. These
|
There are a few other utilities that are often useful in CLI programming. These
|
||||||
|
@ -35,9 +35,9 @@ namespace CLI {
|
|||||||
// [CLI11:app_hpp:verbatim]
|
// [CLI11:app_hpp:verbatim]
|
||||||
|
|
||||||
#ifndef CLI11_PARSE
|
#ifndef CLI11_PARSE
|
||||||
#define CLI11_PARSE(app, argc, argv) \
|
#define CLI11_PARSE(app, ...) \
|
||||||
try { \
|
try { \
|
||||||
(app).parse((argc), (argv)); \
|
(app).parse(__VA_ARGS__); \
|
||||||
} catch(const CLI::ParseError &e) { \
|
} catch(const CLI::ParseError &e) { \
|
||||||
return (app).exit(e); \
|
return (app).exit(e); \
|
||||||
}
|
}
|
||||||
@ -837,15 +837,25 @@ class App {
|
|||||||
/// Reset the parsed data
|
/// Reset the parsed data
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
/// Parse the command-line arguments passed to the main function of the executable.
|
||||||
|
/// This overload will correctly parse unicode arguments on Windows.
|
||||||
|
void parse();
|
||||||
|
|
||||||
/// Parses the command line - throws errors.
|
/// Parses the command line - throws errors.
|
||||||
/// This must be called after the options are in but before the rest of the program.
|
/// This must be called after the options are in but before the rest of the program.
|
||||||
void parse(int argc, const char *const *argv);
|
void parse(int argc, const char *const *argv);
|
||||||
|
void parse(int argc, const wchar_t *const *argv);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <class CharT> void parse_char_t(int argc, const CharT *const *argv);
|
||||||
|
|
||||||
|
public:
|
||||||
/// Parse a single string as if it contained command line arguments.
|
/// Parse a single string as if it contained command line arguments.
|
||||||
/// This function splits the string into arguments then calls parse(std::vector<std::string> &)
|
/// This function splits the string into arguments then calls parse(std::vector<std::string> &)
|
||||||
/// the function takes an optional boolean argument specifying if the programName is included in the string to
|
/// the function takes an optional boolean argument specifying if the programName is included in the string to
|
||||||
/// process
|
/// process
|
||||||
void parse(std::string commandline, bool program_name_included = false);
|
void parse(std::string commandline, bool program_name_included = false);
|
||||||
|
void parse(std::wstring commandline, bool program_name_included = false);
|
||||||
|
|
||||||
/// The real work is done here. Expects a reversed vector.
|
/// The real work is done here. Expects a reversed vector.
|
||||||
/// Changes the vector to the remaining options.
|
/// Changes the vector to the remaining options.
|
||||||
|
25
include/CLI/Argv.hpp
Normal file
25
include/CLI/Argv.hpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <CLI/Macros.hpp>
|
||||||
|
|
||||||
|
namespace CLI {
|
||||||
|
// [CLI11:argv_hpp:verbatim]
|
||||||
|
|
||||||
|
/// argc as passed in to this executable.
|
||||||
|
CLI11_INLINE int argc();
|
||||||
|
|
||||||
|
/// argv as passed in to this executable, converted to utf-8 on Windows.
|
||||||
|
CLI11_INLINE const char *const *argv();
|
||||||
|
|
||||||
|
// [CLI11:argv_hpp:end]
|
||||||
|
} // namespace CLI
|
||||||
|
|
||||||
|
#ifndef CLI11_COMPILE
|
||||||
|
#include "impl/Argv_inl.hpp"
|
||||||
|
#endif
|
@ -13,6 +13,10 @@
|
|||||||
|
|
||||||
#include "Macros.hpp"
|
#include "Macros.hpp"
|
||||||
|
|
||||||
|
#include "Encoding.hpp"
|
||||||
|
|
||||||
|
#include "Argv.hpp"
|
||||||
|
|
||||||
#include "StringTools.hpp"
|
#include "StringTools.hpp"
|
||||||
|
|
||||||
#include "Error.hpp"
|
#include "Error.hpp"
|
||||||
|
54
include/CLI/Encoding.hpp
Normal file
54
include/CLI/Encoding.hpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <CLI/Macros.hpp>
|
||||||
|
|
||||||
|
// [CLI11:public_includes:set]
|
||||||
|
#include <string>
|
||||||
|
// [CLI11:public_includes:end]
|
||||||
|
|
||||||
|
// [CLI11:encoding_includes:verbatim]
|
||||||
|
#ifdef CLI11_CPP17
|
||||||
|
#include <string_view>
|
||||||
|
#endif // CLI11_CPP17
|
||||||
|
|
||||||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||||||
|
#include <filesystem>
|
||||||
|
#include <string_view> // NOLINT(build/include)
|
||||||
|
#endif // CLI11_HAS_FILESYSTEM
|
||||||
|
// [CLI11:encoding_includes:end]
|
||||||
|
|
||||||
|
namespace CLI {
|
||||||
|
// [CLI11:encoding_hpp:verbatim]
|
||||||
|
|
||||||
|
/// Convert a wide string to a narrow string.
|
||||||
|
CLI11_INLINE std::string narrow(const std::wstring &str);
|
||||||
|
CLI11_INLINE std::string narrow(const wchar_t *str);
|
||||||
|
CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t size);
|
||||||
|
|
||||||
|
/// Convert a narrow string to a wide string.
|
||||||
|
CLI11_INLINE std::wstring widen(const std::string &str);
|
||||||
|
CLI11_INLINE std::wstring widen(const char *str);
|
||||||
|
CLI11_INLINE std::wstring widen(const char *str, std::size_t size);
|
||||||
|
|
||||||
|
#ifdef CLI11_CPP17
|
||||||
|
CLI11_INLINE std::string narrow(std::wstring_view str);
|
||||||
|
CLI11_INLINE std::wstring widen(std::string_view str);
|
||||||
|
#endif // CLI11_CPP17
|
||||||
|
|
||||||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||||||
|
/// Convert a char-string to a native path correctly.
|
||||||
|
CLI11_INLINE std::filesystem::path to_path(std::string_view str);
|
||||||
|
#endif // CLI11_HAS_FILESYSTEM
|
||||||
|
|
||||||
|
// [CLI11:encoding_hpp:end]
|
||||||
|
} // namespace CLI
|
||||||
|
|
||||||
|
#ifndef CLI11_COMPILE
|
||||||
|
#include "impl/Encoding_inl.hpp"
|
||||||
|
#endif
|
@ -66,6 +66,62 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/** <filesystem> availability */
|
||||||
|
#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
|
||||||
|
#if __has_include(<filesystem>)
|
||||||
|
// Filesystem cannot be used if targeting macOS < 10.15
|
||||||
|
#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
|
||||||
|
#define CLI11_HAS_FILESYSTEM 0
|
||||||
|
#elif defined(__wasi__)
|
||||||
|
// As of wasi-sdk-14, filesystem is not implemented
|
||||||
|
#define CLI11_HAS_FILESYSTEM 0
|
||||||
|
#else
|
||||||
|
#include <filesystem>
|
||||||
|
#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
|
||||||
|
#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9
|
||||||
|
#define CLI11_HAS_FILESYSTEM 1
|
||||||
|
#elif defined(__GLIBCXX__)
|
||||||
|
// if we are using gcc and Version <9 default to no filesystem
|
||||||
|
#define CLI11_HAS_FILESYSTEM 0
|
||||||
|
#else
|
||||||
|
#define CLI11_HAS_FILESYSTEM 1
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define CLI11_HAS_FILESYSTEM 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** <codecvt> availability */
|
||||||
|
#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 5
|
||||||
|
#define CLI11_HAS_CODECVT 0
|
||||||
|
#else
|
||||||
|
#define CLI11_HAS_CODECVT 1
|
||||||
|
#include <codecvt>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** disable deprecations */
|
||||||
|
#if defined(__GNUC__) // GCC or clang
|
||||||
|
#define CLI11_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
|
||||||
|
#define CLI11_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
|
||||||
|
|
||||||
|
#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
|
||||||
|
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#define CLI11_DIAGNOSTIC_PUSH __pragma(warning(push))
|
||||||
|
#define CLI11_DIAGNOSTIC_POP __pragma(warning(pop))
|
||||||
|
|
||||||
|
#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED __pragma(warning(disable : 4996))
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define CLI11_DIAGNOSTIC_PUSH
|
||||||
|
#define CLI11_DIAGNOSTIC_POP
|
||||||
|
|
||||||
|
#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/** Inline macro **/
|
/** Inline macro **/
|
||||||
#ifdef CLI11_COMPILE
|
#ifdef CLI11_COMPILE
|
||||||
#define CLI11_INLINE
|
#define CLI11_INLINE
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
// [CLI11:public_includes:end]
|
// [CLI11:public_includes:end]
|
||||||
|
|
||||||
|
#include "Encoding.hpp"
|
||||||
#include "StringTools.hpp"
|
#include "StringTools.hpp"
|
||||||
|
|
||||||
namespace CLI {
|
namespace CLI {
|
||||||
@ -242,8 +243,10 @@ struct is_mutable_container<
|
|||||||
decltype(std::declval<T>().clear()),
|
decltype(std::declval<T>().clear()),
|
||||||
decltype(std::declval<T>().insert(std::declval<decltype(std::declval<T>().end())>(),
|
decltype(std::declval<T>().insert(std::declval<decltype(std::declval<T>().end())>(),
|
||||||
std::declval<const typename T::value_type &>()))>,
|
std::declval<const typename T::value_type &>()))>,
|
||||||
void>>
|
void>> : public conditional_t<std::is_constructible<T, std::string>::value ||
|
||||||
: public conditional_t<std::is_constructible<T, std::string>::value, std::false_type, std::true_type> {};
|
std::is_constructible<T, std::wstring>::value,
|
||||||
|
std::false_type,
|
||||||
|
std::true_type> {};
|
||||||
|
|
||||||
// check to see if an object is a mutable container (fail by default)
|
// check to see if an object is a mutable container (fail by default)
|
||||||
template <typename T, typename _ = void> struct is_readable_container : std::false_type {};
|
template <typename T, typename _ = void> struct is_readable_container : std::false_type {};
|
||||||
@ -546,6 +549,8 @@ enum class object_category : int {
|
|||||||
// string like types
|
// string like types
|
||||||
string_assignable = 23,
|
string_assignable = 23,
|
||||||
string_constructible = 24,
|
string_constructible = 24,
|
||||||
|
wstring_assignable = 25,
|
||||||
|
wstring_constructible = 26,
|
||||||
other = 45,
|
other = 45,
|
||||||
// special wrapper or container types
|
// special wrapper or container types
|
||||||
wrapper_value = 50,
|
wrapper_value = 50,
|
||||||
@ -613,6 +618,27 @@ struct classify_object<
|
|||||||
static constexpr object_category value{object_category::string_constructible};
|
static constexpr object_category value{object_category::string_constructible};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Wide strings
|
||||||
|
template <typename T>
|
||||||
|
struct classify_object<T,
|
||||||
|
typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
||||||
|
!std::is_assignable<T &, std::string>::value &&
|
||||||
|
!std::is_constructible<T, std::string>::value &&
|
||||||
|
std::is_assignable<T &, std::wstring>::value>::type> {
|
||||||
|
static constexpr object_category value{object_category::wstring_assignable};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct classify_object<
|
||||||
|
T,
|
||||||
|
typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
||||||
|
!std::is_assignable<T &, std::string>::value &&
|
||||||
|
!std::is_constructible<T, std::string>::value &&
|
||||||
|
!std::is_assignable<T &, std::wstring>::value && (type_count<T>::value == 1) &&
|
||||||
|
std::is_constructible<T, std::wstring>::value>::type> {
|
||||||
|
static constexpr object_category value{object_category::wstring_constructible};
|
||||||
|
};
|
||||||
|
|
||||||
/// Enumerations
|
/// Enumerations
|
||||||
template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
|
template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> {
|
||||||
static constexpr object_category value{object_category::enumeration};
|
static constexpr object_category value{object_category::enumeration};
|
||||||
@ -625,12 +651,13 @@ template <typename T> struct classify_object<T, typename std::enable_if<is_compl
|
|||||||
/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point,
|
/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point,
|
||||||
/// vectors, and enumerations
|
/// vectors, and enumerations
|
||||||
template <typename T> struct uncommon_type {
|
template <typename T> struct uncommon_type {
|
||||||
using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
using type = typename std::conditional<
|
||||||
!std::is_assignable<T &, std::string>::value &&
|
!std::is_floating_point<T>::value && !std::is_integral<T>::value &&
|
||||||
!std::is_constructible<T, std::string>::value && !is_complex<T>::value &&
|
!std::is_assignable<T &, std::string>::value && !std::is_constructible<T, std::string>::value &&
|
||||||
!is_mutable_container<T>::value && !std::is_enum<T>::value,
|
!std::is_assignable<T &, std::wstring>::value && !std::is_constructible<T, std::wstring>::value &&
|
||||||
std::true_type,
|
!is_complex<T>::value && !is_mutable_container<T>::value && !std::is_enum<T>::value,
|
||||||
std::false_type>::type;
|
std::true_type,
|
||||||
|
std::false_type>::type;
|
||||||
static constexpr bool value = type::value;
|
static constexpr bool value = type::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1005,6 +1032,23 @@ bool lexical_cast(const std::string &input, T &output) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wide strings
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
enable_if_t<classify_object<T>::value == object_category::wstring_assignable, detail::enabler> = detail::dummy>
|
||||||
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
|
output = widen(input);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename T,
|
||||||
|
enable_if_t<classify_object<T>::value == object_category::wstring_constructible, detail::enabler> = detail::dummy>
|
||||||
|
bool lexical_cast(const std::string &input, T &output) {
|
||||||
|
output = T{widen(input)};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// Enumerations
|
/// Enumerations
|
||||||
template <typename T,
|
template <typename T,
|
||||||
enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
|
enable_if_t<classify_object<T>::value == object_category::enumeration, detail::enabler> = detail::dummy>
|
||||||
@ -1133,7 +1177,9 @@ template <typename AssignTo,
|
|||||||
typename ConvertTo,
|
typename ConvertTo,
|
||||||
enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&
|
enable_if_t<std::is_same<AssignTo, ConvertTo>::value &&
|
||||||
(classify_object<AssignTo>::value == object_category::string_assignable ||
|
(classify_object<AssignTo>::value == object_category::string_assignable ||
|
||||||
classify_object<AssignTo>::value == object_category::string_constructible),
|
classify_object<AssignTo>::value == object_category::string_constructible ||
|
||||||
|
classify_object<AssignTo>::value == object_category::wstring_assignable ||
|
||||||
|
classify_object<AssignTo>::value == object_category::wstring_constructible),
|
||||||
detail::enabler> = detail::dummy>
|
detail::enabler> = detail::dummy>
|
||||||
bool lexical_assign(const std::string &input, AssignTo &output) {
|
bool lexical_assign(const std::string &input, AssignTo &output) {
|
||||||
return lexical_cast(input, output);
|
return lexical_cast(input, output);
|
||||||
@ -1144,7 +1190,9 @@ template <typename AssignTo,
|
|||||||
typename ConvertTo,
|
typename ConvertTo,
|
||||||
enable_if_t<std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, AssignTo>::value &&
|
enable_if_t<std::is_same<AssignTo, ConvertTo>::value && std::is_assignable<AssignTo &, AssignTo>::value &&
|
||||||
classify_object<AssignTo>::value != object_category::string_assignable &&
|
classify_object<AssignTo>::value != object_category::string_assignable &&
|
||||||
classify_object<AssignTo>::value != object_category::string_constructible,
|
classify_object<AssignTo>::value != object_category::string_constructible &&
|
||||||
|
classify_object<AssignTo>::value != object_category::wstring_assignable &&
|
||||||
|
classify_object<AssignTo>::value != object_category::wstring_constructible,
|
||||||
detail::enabler> = detail::dummy>
|
detail::enabler> = detail::dummy>
|
||||||
bool lexical_assign(const std::string &input, AssignTo &output) {
|
bool lexical_assign(const std::string &input, AssignTo &output) {
|
||||||
if(input.empty()) {
|
if(input.empty()) {
|
||||||
|
@ -26,34 +26,6 @@
|
|||||||
|
|
||||||
// [CLI11:validators_hpp_filesystem:verbatim]
|
// [CLI11:validators_hpp_filesystem:verbatim]
|
||||||
|
|
||||||
// C standard library
|
|
||||||
// Only needed for existence checking
|
|
||||||
#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
|
|
||||||
#if __has_include(<filesystem>)
|
|
||||||
// Filesystem cannot be used if targeting macOS < 10.15
|
|
||||||
#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
|
|
||||||
#define CLI11_HAS_FILESYSTEM 0
|
|
||||||
#elif defined(__wasi__)
|
|
||||||
// As of wasi-sdk-14, filesystem is not implemented
|
|
||||||
#define CLI11_HAS_FILESYSTEM 0
|
|
||||||
#else
|
|
||||||
#include <filesystem>
|
|
||||||
#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
|
|
||||||
#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9
|
|
||||||
#define CLI11_HAS_FILESYSTEM 1
|
|
||||||
#elif defined(__GLIBCXX__)
|
|
||||||
// if we are using gcc and Version <9 default to no filesystem
|
|
||||||
#define CLI11_HAS_FILESYSTEM 0
|
|
||||||
#else
|
|
||||||
#define CLI11_HAS_FILESYSTEM 1
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define CLI11_HAS_FILESYSTEM 0
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||||||
#include <filesystem> // NOLINT(build/include)
|
#include <filesystem> // NOLINT(build/include)
|
||||||
#else
|
#else
|
||||||
|
@ -9,6 +9,9 @@
|
|||||||
// This include is only needed for IDEs to discover symbols
|
// This include is only needed for IDEs to discover symbols
|
||||||
#include <CLI/App.hpp>
|
#include <CLI/App.hpp>
|
||||||
|
|
||||||
|
#include <CLI/Argv.hpp>
|
||||||
|
#include <CLI/Encoding.hpp>
|
||||||
|
|
||||||
// [CLI11:public_includes:set]
|
// [CLI11:public_includes:set]
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -474,17 +477,31 @@ CLI11_INLINE void App::clear() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CLI11_INLINE void App::parse(int argc, const char *const *argv) {
|
CLI11_INLINE void App::parse() { parse(argc(), argv()); } // LCOV_EXCL_LINE
|
||||||
|
|
||||||
|
CLI11_INLINE void App::parse(int argc, const char *const *argv) { parse_char_t(argc, argv); }
|
||||||
|
CLI11_INLINE void App::parse(int argc, const wchar_t *const *argv) { parse_char_t(argc, argv); }
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
// Do nothing or perform narrowing
|
||||||
|
CLI11_INLINE const char *maybe_narrow(const char *str) { return str; }
|
||||||
|
CLI11_INLINE std::string maybe_narrow(const wchar_t *str) { return narrow(str); }
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
template <class CharT> CLI11_INLINE void App::parse_char_t(int argc, const CharT *const *argv) {
|
||||||
// If the name is not set, read from command line
|
// If the name is not set, read from command line
|
||||||
if(name_.empty() || has_automatic_name_) {
|
if(name_.empty() || has_automatic_name_) {
|
||||||
has_automatic_name_ = true;
|
has_automatic_name_ = true;
|
||||||
name_ = argv[0];
|
name_ = detail::maybe_narrow(argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
args.reserve(static_cast<std::size_t>(argc) - 1U);
|
args.reserve(static_cast<std::size_t>(argc) - 1U);
|
||||||
for(auto i = static_cast<std::size_t>(argc) - 1U; i > 0U; --i)
|
for(auto i = static_cast<std::size_t>(argc) - 1U; i > 0U; --i)
|
||||||
args.emplace_back(argv[i]);
|
args.emplace_back(detail::maybe_narrow(argv[i]));
|
||||||
|
|
||||||
parse(std::move(args));
|
parse(std::move(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,6 +532,10 @@ CLI11_INLINE void App::parse(std::string commandline, bool program_name_included
|
|||||||
parse(std::move(args));
|
parse(std::move(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLI11_INLINE void App::parse(std::wstring commandline, bool program_name_included) {
|
||||||
|
parse(narrow(commandline), program_name_included);
|
||||||
|
}
|
||||||
|
|
||||||
CLI11_INLINE void App::parse(std::vector<std::string> &args) {
|
CLI11_INLINE void App::parse(std::vector<std::string> &args) {
|
||||||
// Clear if parsed
|
// Clear if parsed
|
||||||
if(parsed_ > 0)
|
if(parsed_ > 0)
|
||||||
|
162
include/CLI/impl/Argv_inl.hpp
Normal file
162
include/CLI/impl/Argv_inl.hpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This include is only needed for IDEs to discover symbols
|
||||||
|
#include <CLI/Argv.hpp>
|
||||||
|
|
||||||
|
#include <CLI/Encoding.hpp>
|
||||||
|
|
||||||
|
// [CLI11:public_includes:set]
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
// [CLI11:public_includes:end]
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "SlimWindowsH.hpp"
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// [CLI11:argv_inl_includes:verbatim]
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <processenv.h>
|
||||||
|
#include <shellapi.h>
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <crt_externs.h>
|
||||||
|
#endif
|
||||||
|
// [CLI11:argv_inl_includes:end]
|
||||||
|
|
||||||
|
namespace CLI {
|
||||||
|
// [CLI11:argv_inl_hpp:verbatim]
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
// Copy argc and argv as early as possible to avoid modification
|
||||||
|
static const std::vector<const char *> static_args = [] {
|
||||||
|
static const std::vector<std::string> static_args_as_strings = [] {
|
||||||
|
std::vector<std::string> args_as_strings;
|
||||||
|
int argc = *_NSGetArgc();
|
||||||
|
char **argv = *_NSGetArgv();
|
||||||
|
|
||||||
|
args_as_strings.reserve(static_cast<size_t>(argc));
|
||||||
|
for(size_t i = 0; i < static_cast<size_t>(argc); i++) {
|
||||||
|
args_as_strings.push_back(argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return args_as_strings;
|
||||||
|
}();
|
||||||
|
|
||||||
|
std::vector<const char *> static_args_result;
|
||||||
|
static_args_result.reserve(static_args_as_strings.size());
|
||||||
|
|
||||||
|
for(const auto &arg : static_args_as_strings) {
|
||||||
|
static_args_result.push_back(arg.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_args_result;
|
||||||
|
}();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Command-line arguments, as passed in to this executable, converted to utf-8 on Windows.
|
||||||
|
CLI11_INLINE const std::vector<const char *> &args() {
|
||||||
|
// This function uses initialization via lambdas extensively to take advantage of the thread safety of static
|
||||||
|
// variable initialization [stmt.dcl.3]
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static const std::vector<const char *> static_args = [] {
|
||||||
|
static const std::vector<std::string> static_args_as_strings = [] {
|
||||||
|
// On Windows, take arguments from GetCommandLineW and convert them to utf-8.
|
||||||
|
std::vector<std::string> args_as_strings;
|
||||||
|
int argc = 0;
|
||||||
|
|
||||||
|
auto deleter = [](wchar_t **ptr) { LocalFree(ptr); };
|
||||||
|
// NOLINTBEGIN(*-avoid-c-arrays)
|
||||||
|
auto wargv =
|
||||||
|
std::unique_ptr<wchar_t *[], decltype(deleter)>(CommandLineToArgvW(GetCommandLineW(), &argc), deleter);
|
||||||
|
// NOLINTEND(*-avoid-c-arrays)
|
||||||
|
|
||||||
|
if(wargv == nullptr) {
|
||||||
|
throw std::runtime_error("CommandLineToArgvW failed with code " + std::to_string(GetLastError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
args_as_strings.reserve(static_cast<size_t>(argc));
|
||||||
|
for(size_t i = 0; i < static_cast<size_t>(argc); ++i) {
|
||||||
|
args_as_strings.push_back(narrow(wargv[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return args_as_strings;
|
||||||
|
}();
|
||||||
|
|
||||||
|
std::vector<const char *> static_args_result;
|
||||||
|
static_args_result.reserve(static_args_as_strings.size());
|
||||||
|
|
||||||
|
for(const auto &arg : static_args_as_strings) {
|
||||||
|
static_args_result.push_back(arg.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_args_result;
|
||||||
|
}();
|
||||||
|
|
||||||
|
return static_args;
|
||||||
|
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
|
||||||
|
return static_args;
|
||||||
|
|
||||||
|
#else
|
||||||
|
static const std::vector<const char *> static_args = [] {
|
||||||
|
static const std::vector<char> static_cmdline = [] {
|
||||||
|
// On posix, retrieve arguments from /proc/self/cmdline, separated by null terminators.
|
||||||
|
std::vector<char> cmdline;
|
||||||
|
|
||||||
|
auto deleter = [](FILE *f) { std::fclose(f); };
|
||||||
|
std::unique_ptr<FILE, decltype(deleter)> fp_unique(std::fopen("/proc/self/cmdline", "r"), deleter);
|
||||||
|
FILE *fp = fp_unique.get();
|
||||||
|
if(!fp) {
|
||||||
|
throw std::runtime_error("could not open /proc/self/cmdline for reading"); // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = 0;
|
||||||
|
while(std::feof(fp) == 0) {
|
||||||
|
cmdline.resize(size + 128);
|
||||||
|
size += std::fread(cmdline.data() + size, 1, 128, fp);
|
||||||
|
|
||||||
|
if(std::ferror(fp) != 0) {
|
||||||
|
throw std::runtime_error("error during reading /proc/self/cmdline"); // LCOV_EXCL_LINE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmdline.resize(size);
|
||||||
|
|
||||||
|
return cmdline;
|
||||||
|
}();
|
||||||
|
|
||||||
|
std::size_t argc = static_cast<std::size_t>(std::count(static_cmdline.begin(), static_cmdline.end(), '\0'));
|
||||||
|
std::vector<const char *> static_args_result;
|
||||||
|
static_args_result.reserve(argc);
|
||||||
|
|
||||||
|
for(auto it = static_cmdline.begin(); it != static_cmdline.end();
|
||||||
|
it = std::find(it, static_cmdline.end(), '\0') + 1) {
|
||||||
|
static_args_result.push_back(static_cmdline.data() + (it - static_cmdline.begin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_args_result;
|
||||||
|
}();
|
||||||
|
|
||||||
|
return static_args;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
CLI11_INLINE const char *const *argv() { return detail::args().data(); }
|
||||||
|
CLI11_INLINE int argc() { return static_cast<int>(detail::args().size()); }
|
||||||
|
|
||||||
|
// [CLI11:argv_inl_hpp:end]
|
||||||
|
} // namespace CLI
|
154
include/CLI/impl/Encoding_inl.hpp
Normal file
154
include/CLI/impl/Encoding_inl.hpp
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This include is only needed for IDEs to discover symbols
|
||||||
|
#include <CLI/Encoding.hpp>
|
||||||
|
#include <CLI/Macros.hpp>
|
||||||
|
|
||||||
|
// [CLI11:public_includes:set]
|
||||||
|
#include <array>
|
||||||
|
#include <clocale>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cwchar>
|
||||||
|
#include <locale>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
// [CLI11:public_includes:end]
|
||||||
|
|
||||||
|
namespace CLI {
|
||||||
|
// [CLI11:encoding_inl_hpp:verbatim]
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
#if !CLI11_HAS_CODECVT
|
||||||
|
/// Attempt to set one of the acceptable unicode locales for conversion
|
||||||
|
CLI11_INLINE void set_unicode_locale() {
|
||||||
|
static const std::array<const char *, 3> unicode_locales{{"C.UTF-8", "en_US.UTF-8", ".UTF-8"}};
|
||||||
|
|
||||||
|
for(const auto &locale_name : unicode_locales) {
|
||||||
|
if(std::setlocale(LC_ALL, locale_name) != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("CLI::narrow: could not set locale to C.UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F> struct scope_guard_t {
|
||||||
|
F closure;
|
||||||
|
|
||||||
|
explicit scope_guard_t(F closure_) : closure(closure_) {}
|
||||||
|
~scope_guard_t() { closure(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename F> CLI11_NODISCARD CLI11_INLINE scope_guard_t<F> scope_guard(F &&closure) {
|
||||||
|
return scope_guard_t<F>{std::forward<F>(closure)};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !CLI11_HAS_CODECVT
|
||||||
|
|
||||||
|
CLI11_DIAGNOSTIC_PUSH
|
||||||
|
CLI11_DIAGNOSTIC_IGNORE_DEPRECATED
|
||||||
|
|
||||||
|
CLI11_INLINE std::string narrow_impl(const wchar_t *str, std::size_t str_size) {
|
||||||
|
#if CLI11_HAS_CODECVT
|
||||||
|
#ifdef _WIN32
|
||||||
|
return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(str, str + str_size);
|
||||||
|
|
||||||
|
#else
|
||||||
|
return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
|
#else // CLI11_HAS_CODECVT
|
||||||
|
(void)str_size;
|
||||||
|
std::mbstate_t state = std::mbstate_t();
|
||||||
|
const wchar_t *it = str;
|
||||||
|
|
||||||
|
std::string old_locale = std::setlocale(LC_ALL, nullptr);
|
||||||
|
auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
|
||||||
|
set_unicode_locale();
|
||||||
|
|
||||||
|
std::size_t new_size = std::wcsrtombs(nullptr, &it, 0, &state);
|
||||||
|
if(new_size == static_cast<std::size_t>(-1)) {
|
||||||
|
throw std::runtime_error("CLI::narrow: conversion error in std::wcsrtombs at offset " +
|
||||||
|
std::to_string(it - str));
|
||||||
|
}
|
||||||
|
std::string result(new_size, '\0');
|
||||||
|
std::wcsrtombs(const_cast<char *>(result.data()), &str, new_size, &state);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
#endif // CLI11_HAS_CODECVT
|
||||||
|
}
|
||||||
|
|
||||||
|
CLI11_INLINE std::wstring widen_impl(const char *str, std::size_t str_size) {
|
||||||
|
#if CLI11_HAS_CODECVT
|
||||||
|
#ifdef _WIN32
|
||||||
|
return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(str, str + str_size);
|
||||||
|
|
||||||
|
#else
|
||||||
|
return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
|
#else // CLI11_HAS_CODECVT
|
||||||
|
(void)str_size;
|
||||||
|
std::mbstate_t state = std::mbstate_t();
|
||||||
|
const char *it = str;
|
||||||
|
|
||||||
|
std::string old_locale = std::setlocale(LC_ALL, nullptr);
|
||||||
|
auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
|
||||||
|
set_unicode_locale();
|
||||||
|
|
||||||
|
std::size_t new_size = std::mbsrtowcs(nullptr, &it, 0, &state);
|
||||||
|
if(new_size == static_cast<std::size_t>(-1)) {
|
||||||
|
throw std::runtime_error("CLI::widen: conversion error in std::mbsrtowcs at offset " +
|
||||||
|
std::to_string(it - str));
|
||||||
|
}
|
||||||
|
std::wstring result(new_size, L'\0');
|
||||||
|
std::mbsrtowcs(const_cast<wchar_t *>(result.data()), &str, new_size, &state);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
#endif // CLI11_HAS_CODECVT
|
||||||
|
}
|
||||||
|
|
||||||
|
CLI11_DIAGNOSTIC_POP
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t str_size) { return detail::narrow_impl(str, str_size); }
|
||||||
|
CLI11_INLINE std::string narrow(const std::wstring &str) { return detail::narrow_impl(str.data(), str.size()); }
|
||||||
|
// Flawfinder: ignore
|
||||||
|
CLI11_INLINE std::string narrow(const wchar_t *str) { return detail::narrow_impl(str, std::wcslen(str)); }
|
||||||
|
|
||||||
|
CLI11_INLINE std::wstring widen(const char *str, std::size_t str_size) { return detail::widen_impl(str, str_size); }
|
||||||
|
CLI11_INLINE std::wstring widen(const std::string &str) { return detail::widen_impl(str.data(), str.size()); }
|
||||||
|
// Flawfinder: ignore
|
||||||
|
CLI11_INLINE std::wstring widen(const char *str) { return detail::widen_impl(str, std::strlen(str)); }
|
||||||
|
|
||||||
|
#ifdef CLI11_CPP17
|
||||||
|
CLI11_INLINE std::string narrow(std::wstring_view str) { return detail::narrow_impl(str.data(), str.size()); }
|
||||||
|
CLI11_INLINE std::wstring widen(std::string_view str) { return detail::widen_impl(str.data(), str.size()); }
|
||||||
|
#endif // CLI11_CPP17
|
||||||
|
|
||||||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||||||
|
CLI11_INLINE std::filesystem::path to_path(std::string_view str) {
|
||||||
|
return std::filesystem::path{
|
||||||
|
#ifdef _WIN32
|
||||||
|
widen(str)
|
||||||
|
#else
|
||||||
|
str
|
||||||
|
#endif // _WIN32
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif // CLI11_HAS_FILESYSTEM
|
||||||
|
|
||||||
|
// [CLI11:encoding_inl_hpp:end]
|
||||||
|
} // namespace CLI
|
101
include/CLI/impl/SlimWindowsH.hpp
Normal file
101
include/CLI/impl/SlimWindowsH.hpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
// [CLI11:slim_windows_h_hpp:verbatim]
|
||||||
|
#ifdef _WIN32
|
||||||
|
// The most slimmed-down version of Windows.h.
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_EXTRA_LEAN
|
||||||
|
|
||||||
|
// Enable components based on necessity.
|
||||||
|
#define NOGDICAPMASKS
|
||||||
|
#define NOVIRTUALKEYCODES
|
||||||
|
#define NOWINMESSAGES
|
||||||
|
#define NOWINSTYLES
|
||||||
|
#define NOSYSMETRICS
|
||||||
|
#define NOMENUS
|
||||||
|
#define NOICONS
|
||||||
|
#define NOKEYSTATES
|
||||||
|
#define NOSYSCOMMANDS
|
||||||
|
#define NORASTEROPS
|
||||||
|
#define NOSHOWWINDOW
|
||||||
|
#define OEMRESOURCE
|
||||||
|
#define NOATOM
|
||||||
|
#define NOCLIPBOARD
|
||||||
|
#define NOCOLOR
|
||||||
|
#define NOCTLMGR
|
||||||
|
#define NODRAWTEXT
|
||||||
|
#define NOGDI
|
||||||
|
#define NOKERNEL
|
||||||
|
#define NOUSER
|
||||||
|
#define NONLS
|
||||||
|
#define NOMB
|
||||||
|
#define NOMEMMGR
|
||||||
|
#define NOMETAFILE
|
||||||
|
#define NOMINMAX
|
||||||
|
#define NOMSG
|
||||||
|
#define NOOPENFILE
|
||||||
|
#define NOSCROLL
|
||||||
|
#define NOSERVICE
|
||||||
|
#define NOSOUND
|
||||||
|
#define NOTEXTMETRIC
|
||||||
|
#define NOWH
|
||||||
|
#define NOWINOFFSETS
|
||||||
|
#define NOCOMM
|
||||||
|
#define NOKANJI
|
||||||
|
#define NOHELP
|
||||||
|
#define NOPROFILER
|
||||||
|
#define NODEFERWINDOWPOS
|
||||||
|
#define NOMCX
|
||||||
|
|
||||||
|
#include "Windows.h"
|
||||||
|
|
||||||
|
#undef WIN32_LEAN_AND_MEAN
|
||||||
|
#undef WIN32_EXTRA_LEAN
|
||||||
|
|
||||||
|
#undef NOGDICAPMASKS
|
||||||
|
#undef NOVIRTUALKEYCODES
|
||||||
|
#undef NOWINMESSAGES
|
||||||
|
#undef NOWINSTYLES
|
||||||
|
#undef NOSYSMETRICS
|
||||||
|
#undef NOMENUS
|
||||||
|
#undef NOICONS
|
||||||
|
#undef NOKEYSTATES
|
||||||
|
#undef NOSYSCOMMANDS
|
||||||
|
#undef NORASTEROPS
|
||||||
|
#undef NOSHOWWINDOW
|
||||||
|
#undef OEMRESOURCE
|
||||||
|
#undef NOATOM
|
||||||
|
#undef NOCLIPBOARD
|
||||||
|
#undef NOCOLOR
|
||||||
|
#undef NOCTLMGR
|
||||||
|
#undef NODRAWTEXT
|
||||||
|
#undef NOGDI
|
||||||
|
#undef NOKERNEL
|
||||||
|
#undef NOUSER
|
||||||
|
#undef NONLS
|
||||||
|
#undef NOMB
|
||||||
|
#undef NOMEMMGR
|
||||||
|
#undef NOMETAFILE
|
||||||
|
#undef NOMINMAX
|
||||||
|
#undef NOMSG
|
||||||
|
#undef NOOPENFILE
|
||||||
|
#undef NOSCROLL
|
||||||
|
#undef NOSERVICE
|
||||||
|
#undef NOSOUND
|
||||||
|
#undef NOTEXTMETRIC
|
||||||
|
#undef NOWH
|
||||||
|
#undef NOWINOFFSETS
|
||||||
|
#undef NOCOMM
|
||||||
|
#undef NOKANJI
|
||||||
|
#undef NOHELP
|
||||||
|
#undef NOPROFILER
|
||||||
|
#undef NODEFERWINDOWPOS
|
||||||
|
#undef NOMCX
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
|
// [CLI11:slim_windows_h_hpp:end]
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <CLI/Validators.hpp>
|
#include <CLI/Validators.hpp>
|
||||||
|
|
||||||
|
#include <CLI/Encoding.hpp>
|
||||||
#include <CLI/Macros.hpp>
|
#include <CLI/Macros.hpp>
|
||||||
#include <CLI/StringTools.hpp>
|
#include <CLI/StringTools.hpp>
|
||||||
#include <CLI/TypeTools.hpp>
|
#include <CLI/TypeTools.hpp>
|
||||||
@ -127,7 +128,7 @@ namespace detail {
|
|||||||
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||||||
CLI11_INLINE path_type check_path(const char *file) noexcept {
|
CLI11_INLINE path_type check_path(const char *file) noexcept {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
auto stat = std::filesystem::status(file, ec);
|
auto stat = std::filesystem::status(to_path(file), ec);
|
||||||
if(ec) {
|
if(ec) {
|
||||||
return path_type::nonexistent;
|
return path_type::nonexistent;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,9 @@ set(CLI11_headers
|
|||||||
${CLI11_headerLoc}/StringTools.hpp
|
${CLI11_headerLoc}/StringTools.hpp
|
||||||
${CLI11_headerLoc}/TypeTools.hpp
|
${CLI11_headerLoc}/TypeTools.hpp
|
||||||
${CLI11_headerLoc}/Validators.hpp
|
${CLI11_headerLoc}/Validators.hpp
|
||||||
${CLI11_headerLoc}/Version.hpp)
|
${CLI11_headerLoc}/Version.hpp
|
||||||
|
${CLI11_headerLoc}/Encoding.hpp
|
||||||
|
${CLI11_headerLoc}/Argv.hpp)
|
||||||
|
|
||||||
set(CLI11_implLoc "${PROJECT_SOURCE_DIR}/include/CLI/impl")
|
set(CLI11_implLoc "${PROJECT_SOURCE_DIR}/include/CLI/impl")
|
||||||
|
|
||||||
@ -24,7 +26,10 @@ set(CLI11_impl_headers
|
|||||||
${CLI11_implLoc}/Option_inl.hpp
|
${CLI11_implLoc}/Option_inl.hpp
|
||||||
${CLI11_implLoc}/Split_inl.hpp
|
${CLI11_implLoc}/Split_inl.hpp
|
||||||
${CLI11_implLoc}/StringTools_inl.hpp
|
${CLI11_implLoc}/StringTools_inl.hpp
|
||||||
${CLI11_implLoc}/Validators_inl.hpp)
|
${CLI11_implLoc}/Validators_inl.hpp
|
||||||
|
${CLI11_implLoc}/Encoding_inl.hpp
|
||||||
|
${CLI11_implLoc}/Argv_inl.hpp
|
||||||
|
${CLI11_implLoc}/SlimWindowsH.hpp)
|
||||||
|
|
||||||
set(CLI11_library_headers ${CLI11_headerLoc}/CLI.hpp ${CLI11_headerLoc}/Timer.hpp)
|
set(CLI11_library_headers ${CLI11_headerLoc}/CLI.hpp ${CLI11_headerLoc}/Timer.hpp)
|
||||||
|
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
// SPDX-License-Identifier: BSD-3-Clause
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
#include <CLI/impl/App_inl.hpp>
|
#include <CLI/impl/App_inl.hpp>
|
||||||
|
#include <CLI/impl/Argv_inl.hpp>
|
||||||
#include <CLI/impl/Config_inl.hpp>
|
#include <CLI/impl/Config_inl.hpp>
|
||||||
|
#include <CLI/impl/Encoding_inl.hpp>
|
||||||
#include <CLI/impl/Formatter_inl.hpp>
|
#include <CLI/impl/Formatter_inl.hpp>
|
||||||
#include <CLI/impl/Option_inl.hpp>
|
#include <CLI/impl/Option_inl.hpp>
|
||||||
#include <CLI/impl/Split_inl.hpp>
|
#include <CLI/impl/Split_inl.hpp>
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "app_helper.hpp"
|
#include "app_helper.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
@ -261,6 +262,28 @@ TEST_CASE_METHOD(TApp, "OneString", "[app]") {
|
|||||||
CHECK("mystring" == str);
|
CHECK("mystring" == str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "OneWideString", "[app]") {
|
||||||
|
std::wstring str;
|
||||||
|
app.add_option("-s,--string", str);
|
||||||
|
args = {"--string", "mystring"};
|
||||||
|
run();
|
||||||
|
CHECK(app.count("-s") == 1u);
|
||||||
|
CHECK(app.count("--string") == 1u);
|
||||||
|
CHECK(L"mystring" == str);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "OneStringWideInput", "[app][unicode]") {
|
||||||
|
std::string str;
|
||||||
|
app.add_option("-s,--string", str);
|
||||||
|
|
||||||
|
std::array<const wchar_t *, 3> cmdline{{L"app", L"--string", L"mystring"}};
|
||||||
|
app.parse(static_cast<int>(cmdline.size()), cmdline.data());
|
||||||
|
|
||||||
|
CHECK(app.count("-s") == 1u);
|
||||||
|
CHECK(app.count("--string") == 1u);
|
||||||
|
CHECK("mystring" == str);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "OneStringWindowsStyle", "[app]") {
|
TEST_CASE_METHOD(TApp, "OneStringWindowsStyle", "[app]") {
|
||||||
std::string str;
|
std::string str;
|
||||||
app.add_option("-s,--string", str);
|
app.add_option("-s,--string", str);
|
||||||
@ -282,6 +305,16 @@ TEST_CASE_METHOD(TApp, "OneStringSingleStringInput", "[app]") {
|
|||||||
CHECK("mystring" == str);
|
CHECK("mystring" == str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(TApp, "OneStringSingleWideStringInput", "[app][unicode]") {
|
||||||
|
std::string str;
|
||||||
|
app.add_option("-s,--string", str);
|
||||||
|
|
||||||
|
app.parse(L"--string mystring");
|
||||||
|
CHECK(app.count("-s") == 1u);
|
||||||
|
CHECK(app.count("--string") == 1u);
|
||||||
|
CHECK("mystring" == str);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(TApp, "OneStringEqualVersion", "[app]") {
|
TEST_CASE_METHOD(TApp, "OneStringEqualVersion", "[app]") {
|
||||||
std::string str;
|
std::string str;
|
||||||
app.add_option("-s,--string", str);
|
app.add_option("-s,--string", str);
|
||||||
@ -2463,3 +2496,21 @@ TEST_CASE("C20_compile", "simple") {
|
|||||||
app.parse("--flag");
|
app.parse("--flag");
|
||||||
CHECK_FALSE(flag->empty());
|
CHECK_FALSE(flag->empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #14
|
||||||
|
TEST_CASE("System Args", "[app]") {
|
||||||
|
const char *commandline = CLI11_SYSTEM_ARGS_EXE " 1234 false \"hello world\"";
|
||||||
|
int retval = std::system(commandline);
|
||||||
|
|
||||||
|
if(retval == -1) {
|
||||||
|
FAIL("Executable '" << commandline << "' reported different argc count");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(retval > 0) {
|
||||||
|
FAIL("Executable '" << commandline << "' reported different argv at index " << (retval - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(retval != 0) {
|
||||||
|
FAIL("Executable '" << commandline << "' failed with an unknown return code");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -49,7 +49,8 @@ set(CLI11_TESTS
|
|||||||
StringParseTest
|
StringParseTest
|
||||||
ComplexTypeTest
|
ComplexTypeTest
|
||||||
TrueFalseTest
|
TrueFalseTest
|
||||||
OptionGroupTest)
|
OptionGroupTest
|
||||||
|
EncodingTest)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND CLI11_TESTS WindowsTest)
|
list(APPEND CLI11_TESTS WindowsTest)
|
||||||
@ -89,6 +90,40 @@ else()
|
|||||||
target_include_directories(catch_main PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
|
target_include_directories(catch_main PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Add special target that copies the data directory for tests
|
||||||
|
file(
|
||||||
|
GLOB_RECURSE DATA_FILES
|
||||||
|
LIST_DIRECTORIES false
|
||||||
|
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/data/*")
|
||||||
|
|
||||||
|
foreach(DATA_FILE IN LISTS DATA_FILES)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${DATA_FILE}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/${DATA_FILE}"
|
||||||
|
"${CMAKE_CURRENT_BINARY_DIR}/${DATA_FILE}"
|
||||||
|
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${DATA_FILE}"
|
||||||
|
VERBATIM)
|
||||||
|
target_sources(catch_main PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/${DATA_FILE}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Build dependent applications which are launched from test code
|
||||||
|
set(CLI11_DEPENDENT_APPLICATIONS system_args)
|
||||||
|
|
||||||
|
foreach(APP IN LISTS CLI11_DEPENDENT_APPLICATIONS)
|
||||||
|
add_executable(${APP} applications/${APP}.cpp)
|
||||||
|
target_include_directories(${APP} PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||||
|
add_dependencies(catch_main ${APP})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
function(add_dependent_application_definitions TARGET)
|
||||||
|
foreach(APP IN LISTS CLI11_DEPENDENT_APPLICATIONS)
|
||||||
|
string(TOUPPER ${APP} APP_UPPERCASE)
|
||||||
|
target_compile_definitions(${TARGET}
|
||||||
|
PRIVATE CLI11_${APP_UPPERCASE}_EXE="$<TARGET_FILE:${APP}>")
|
||||||
|
endforeach()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
# Target must already exist
|
# Target must already exist
|
||||||
macro(add_catch_test TESTNAME)
|
macro(add_catch_test TESTNAME)
|
||||||
target_link_libraries(${TESTNAME} PUBLIC catch_main)
|
target_link_libraries(${TESTNAME} PUBLIC catch_main)
|
||||||
@ -108,6 +143,8 @@ foreach(T IN LISTS CLI11_TESTS)
|
|||||||
set_property(SOURCE ${T}.cpp PROPERTY LANGUAGE CUDA)
|
set_property(SOURCE ${T}.cpp PROPERTY LANGUAGE CUDA)
|
||||||
endif()
|
endif()
|
||||||
add_executable(${T} ${T}.cpp)
|
add_executable(${T} ${T}.cpp)
|
||||||
|
|
||||||
|
add_dependent_application_definitions(${T})
|
||||||
add_sanitizers(${T})
|
add_sanitizers(${T})
|
||||||
if(NOT CLI11_CUDA_TESTS)
|
if(NOT CLI11_CUDA_TESTS)
|
||||||
target_link_libraries(${T} PRIVATE CLI11_warnings)
|
target_link_libraries(${T} PRIVATE CLI11_warnings)
|
||||||
@ -117,6 +154,7 @@ foreach(T IN LISTS CLI11_TESTS)
|
|||||||
|
|
||||||
if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
|
if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
|
||||||
add_executable(${T}_Single ${T}.cpp)
|
add_executable(${T}_Single ${T}.cpp)
|
||||||
|
add_dependent_application_definitions(${T}_Single)
|
||||||
target_link_libraries(${T}_Single PRIVATE CLI11_SINGLE)
|
target_link_libraries(${T}_Single PRIVATE CLI11_SINGLE)
|
||||||
add_catch_test(${T}_Single)
|
add_catch_test(${T}_Single)
|
||||||
set_property(TARGET ${T}_Single PROPERTY FOLDER "Tests Single File")
|
set_property(TARGET ${T}_Single PROPERTY FOLDER "Tests Single File")
|
||||||
@ -125,6 +163,7 @@ endforeach()
|
|||||||
|
|
||||||
foreach(T IN LISTS CLI11_MULTIONLY_TESTS)
|
foreach(T IN LISTS CLI11_MULTIONLY_TESTS)
|
||||||
add_executable(${T} ${T}.cpp)
|
add_executable(${T} ${T}.cpp)
|
||||||
|
add_dependent_application_definitions(${T})
|
||||||
add_sanitizers(${T})
|
add_sanitizers(${T})
|
||||||
target_link_libraries(${T} PUBLIC CLI11)
|
target_link_libraries(${T} PUBLIC CLI11)
|
||||||
add_catch_test(${T})
|
add_catch_test(${T})
|
||||||
|
104
tests/EncodingTest.cpp
Normal file
104
tests/EncodingTest.cpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include "app_helper.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||||||
|
#include <filesystem>
|
||||||
|
#endif // CLI11_HAS_FILESYSTEM
|
||||||
|
|
||||||
|
// "abcd"
|
||||||
|
static const std::string abcd_str = "abcd"; // NOLINT(runtime/string)
|
||||||
|
static const std::wstring abcd_wstr = L"abcd"; // NOLINT(runtime/string)
|
||||||
|
|
||||||
|
// "𓂀𓂀𓂀" - 4-byte utf8 characters
|
||||||
|
static const std::array<uint8_t, 12 + 1> egypt_utf8_codeunits{
|
||||||
|
{0xF0, 0x93, 0x82, 0x80, 0xF0, 0x93, 0x82, 0x80, 0xF0, 0x93, 0x82, 0x80}};
|
||||||
|
static const std::string egypt_str(reinterpret_cast<const char *>(egypt_utf8_codeunits.data()));
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static const std::array<uint16_t, 6 + 1> egypt_utf16_codeunits{{0xD80C, 0xDC80, 0xD80C, 0xDC80, 0xD80C, 0xDC80}};
|
||||||
|
static const std::wstring egypt_wstr(reinterpret_cast<const wchar_t *>(egypt_utf16_codeunits.data()));
|
||||||
|
|
||||||
|
#else
|
||||||
|
static const std::array<uint32_t, 3 + 1> egypt_utf32_codeunits{{0x00013080, 0x00013080, 0x00013080}};
|
||||||
|
static const std::wstring egypt_wstr(reinterpret_cast<const wchar_t *>(egypt_utf32_codeunits.data()));
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// "Hello Halló Привет 你好 👩🚀❤️" - many languages and complex emojis
|
||||||
|
static const std::array<uint8_t, 50 + 1> hello_utf8_codeunits{
|
||||||
|
{0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x48, 0x61, 0x6c, 0x6c, 0xc3, 0xb3, 0x20, 0xd0, 0x9f, 0xd1, 0x80,
|
||||||
|
0xd0, 0xb8, 0xd0, 0xb2, 0xd0, 0xb5, 0xd1, 0x82, 0x20, 0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd, 0x20, 0xf0,
|
||||||
|
0x9f, 0x91, 0xa9, 0xe2, 0x80, 0x8d, 0xf0, 0x9f, 0x9a, 0x80, 0xe2, 0x9d, 0xa4, 0xef, 0xb8, 0x8f}};
|
||||||
|
static const std::string hello_str(reinterpret_cast<const char *>(hello_utf8_codeunits.data()));
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static const std::array<uint16_t, 29 + 1> hello_utf16_codeunits{
|
||||||
|
{0x0048, 0x0065, 0x006c, 0x006c, 0x006f, 0x0020, 0x0048, 0x0061, 0x006c, 0x006c,
|
||||||
|
0x00f3, 0x0020, 0x041f, 0x0440, 0x0438, 0x0432, 0x0435, 0x0442, 0x0020, 0x4f60,
|
||||||
|
0x597d, 0x0020, 0xd83d, 0xdc69, 0x200d, 0xd83d, 0xde80, 0x2764, 0xfe0f}};
|
||||||
|
static const std::wstring hello_wstr(reinterpret_cast<const wchar_t *>(hello_utf16_codeunits.data()));
|
||||||
|
|
||||||
|
#else
|
||||||
|
static const std::array<uint32_t, 27 + 1> hello_utf32_codeunits{
|
||||||
|
{0x00000048, 0x00000065, 0x0000006c, 0x0000006c, 0x0000006f, 0x00000020, 0x00000048, 0x00000061, 0x0000006c,
|
||||||
|
0x0000006c, 0x000000f3, 0x00000020, 0x0000041f, 0x00000440, 0x00000438, 0x00000432, 0x00000435, 0x00000442,
|
||||||
|
0x00000020, 0x00004f60, 0x0000597d, 0x00000020, 0x0001f469, 0x0000200d, 0x0001f680, 0x00002764, 0x0000fe0f}};
|
||||||
|
static const std::wstring hello_wstr(reinterpret_cast<const wchar_t *>(hello_utf32_codeunits.data()));
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// #14
|
||||||
|
TEST_CASE("Encoding: Widen", "[unicode]") {
|
||||||
|
using CLI::widen;
|
||||||
|
|
||||||
|
CHECK(abcd_wstr == widen(abcd_str));
|
||||||
|
CHECK(egypt_wstr == widen(egypt_str));
|
||||||
|
CHECK(hello_wstr == widen(hello_str));
|
||||||
|
|
||||||
|
CHECK(hello_wstr == widen(hello_str.c_str()));
|
||||||
|
CHECK(hello_wstr == widen(hello_str.c_str(), hello_str.size()));
|
||||||
|
|
||||||
|
#ifdef CLI11_CPP17
|
||||||
|
CHECK(hello_wstr == widen(std::string_view{hello_str}));
|
||||||
|
#endif // CLI11_CPP17
|
||||||
|
}
|
||||||
|
|
||||||
|
// #14
|
||||||
|
TEST_CASE("Encoding: Narrow", "[unicode]") {
|
||||||
|
using CLI::narrow;
|
||||||
|
|
||||||
|
CHECK(abcd_str == narrow(abcd_wstr));
|
||||||
|
CHECK(egypt_str == narrow(egypt_wstr));
|
||||||
|
CHECK(hello_str == narrow(hello_wstr));
|
||||||
|
|
||||||
|
CHECK(hello_str == narrow(hello_wstr.c_str()));
|
||||||
|
CHECK(hello_str == narrow(hello_wstr.c_str(), hello_wstr.size()));
|
||||||
|
|
||||||
|
#ifdef CLI11_CPP17
|
||||||
|
CHECK(hello_str == narrow(std::wstring_view{hello_wstr}));
|
||||||
|
#endif // CLI11_CPP17
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||||||
|
// #14
|
||||||
|
TEST_CASE("Encoding: to_path roundtrip", "[unicode]") {
|
||||||
|
using std::filesystem::path;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::wstring native_str = CLI::widen(hello_str);
|
||||||
|
#else
|
||||||
|
std::string native_str = hello_str;
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
CHECK(CLI::to_path(hello_str).native() == native_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CLI11_HAS_FILESYSTEM
|
@ -13,6 +13,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include <array>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -67,3 +70,54 @@ inline void unset_env(std::string name) {
|
|||||||
unsetenv(name.c_str());
|
unsetenv(name.c_str());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CLI11_INLINE void check_identical_files(const char *path1, const char *path2) {
|
||||||
|
std::string err1 = CLI::ExistingFile(path1);
|
||||||
|
if(!err1.empty()) {
|
||||||
|
FAIL("Could not open " << path1 << ": " << err1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string err2 = CLI::ExistingFile(path2);
|
||||||
|
if(!err2.empty()) {
|
||||||
|
FAIL("Could not open " << path2 << ": " << err2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// open files at the end to compare size first
|
||||||
|
std::ifstream file1(path1, std::ifstream::ate | std::ifstream::binary);
|
||||||
|
std::ifstream file2(path2, std::ifstream::ate | std::ifstream::binary);
|
||||||
|
|
||||||
|
if(!file1.good()) {
|
||||||
|
FAIL("File " << path1 << " is corrupted");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!file2.good()) {
|
||||||
|
FAIL("File " << path2 << " is corrupted");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file1.tellg() != file2.tellg()) {
|
||||||
|
FAIL("Different file sizes:\n " << file1.tellg() << " bytes in " << path1 << "\n " << file2.tellg()
|
||||||
|
<< " bytes in " << path2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// rewind files
|
||||||
|
file1.seekg(0);
|
||||||
|
file2.seekg(0);
|
||||||
|
|
||||||
|
std::array<uint8_t, 10240> buffer1;
|
||||||
|
std::array<uint8_t, 10240> buffer2;
|
||||||
|
|
||||||
|
for(size_t ibuffer = 0; file1.good(); ++ibuffer) {
|
||||||
|
// Flawfinder: ignore
|
||||||
|
file1.read(reinterpret_cast<char *>(buffer1.data()), static_cast<std::streamsize>(buffer1.size()));
|
||||||
|
// Flawfinder: ignore
|
||||||
|
file2.read(reinterpret_cast<char *>(buffer2.data()), static_cast<std::streamsize>(buffer2.size()));
|
||||||
|
|
||||||
|
for(size_t i = 0; i < static_cast<size_t>(file1.gcount()); ++i) {
|
||||||
|
if(buffer1[i] != buffer2[i]) {
|
||||||
|
FAIL(std::hex << std::setfill('0') << "Different bytes at position " << (ibuffer * 10240 + i) << ":\n "
|
||||||
|
<< "0x" << std::setw(2) << static_cast<int>(buffer1[i]) << " in " << path1 << "\n "
|
||||||
|
<< "0x" << std::setw(2) << static_cast<int>(buffer2[i]) << " in " << path2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
22
tests/applications/system_args.cpp
Normal file
22
tests/applications/system_args.cpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright (c) 2017-2023, University of Cincinnati, developed by Henry Schreiner
|
||||||
|
// under NSF AWARD 1414736 and by the respective contributors.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#include <CLI/CLI.hpp>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
if(argc != CLI::argc()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < argc; i++) {
|
||||||
|
if(std::strcmp(argv[i], CLI::argv()[i]) != 0) {
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
1
tests/data/unicode.txt
Normal file
1
tests/data/unicode.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello Halló Привет 你好 👩🚀❤️
|
@ -57,6 +57,20 @@ testnames = [
|
|||||||
['link_test_2', {'link_with': link_test_lib}],
|
['link_test_2', {'link_with': link_test_lib}],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
dependent_applications = [
|
||||||
|
'system_args'
|
||||||
|
]
|
||||||
|
dependent_applications_definitions = []
|
||||||
|
#dependent_applications_targets = []
|
||||||
|
foreach app: dependent_applications
|
||||||
|
app_target = executable(app, 'applications'/app + '.cpp',
|
||||||
|
build_by_default: false
|
||||||
|
)
|
||||||
|
|
||||||
|
#dependent_applications_targets += dependency(app_target)
|
||||||
|
dependent_applications_definitions += '-DCLI11_@0@_EXE="@1@"'.format(app.to_upper(), app_target)
|
||||||
|
endforeach
|
||||||
|
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
testnames += [['WindowsTest', {}]]
|
testnames += [['WindowsTest', {}]]
|
||||||
endif
|
endif
|
||||||
@ -69,7 +83,7 @@ foreach n: testnames
|
|||||||
name = n[0]
|
name = n[0]
|
||||||
kwargs = n[1]
|
kwargs = n[1]
|
||||||
t = executable(name, name + '.cpp',
|
t = executable(name, name + '.cpp',
|
||||||
cpp_args: kwargs.get('cpp_args', []),
|
cpp_args: kwargs.get('cpp_args', []) + dependent_applications_definitions,
|
||||||
build_by_default: false,
|
build_by_default: false,
|
||||||
dependencies: [testdep] + kwargs.get('dependencies', []),
|
dependencies: [testdep] + kwargs.get('dependencies', []),
|
||||||
link_with: kwargs.get('link_with', [])
|
link_with: kwargs.get('link_with', [])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user