aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2017-06-20 18:22:00 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2017-06-22 01:38:49 +0300
commit06b2428c2afaa36997a9d341bd497d42aa0fef89 (patch)
treeff83476738222578a19b59419fdbaa79a05156d7
parentREADME update (diff)
downloadaes-tools-06b2428c2afaa36997a9d341bd497d42aa0fef89.tar.gz
aes-tools-06b2428c2afaa36997a9d341bd497d42aa0fef89.zip
utils: refactor command-line arguments parsing
+ update usage messages.
-rw-r--r--utils/CMakeLists.txt4
-rw-r--r--utils/block_cmd_parser.hpp203
-rw-r--r--utils/decrypt_block.cpp65
-rw-r--r--utils/decrypt_bmp.cpp32
-rw-r--r--utils/decrypt_file.cpp32
-rw-r--r--utils/encrypt_block.cpp65
-rw-r--r--utils/encrypt_bmp.cpp32
-rw-r--r--utils/encrypt_file.cpp32
-rw-r--r--utils/file_cmd_parser.hpp129
-rw-r--r--utils/helpers/command_line.hpp86
10 files changed, 353 insertions, 327 deletions
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
index 0f3590b..6b7a1c4 100644
--- a/utils/CMakeLists.txt
+++ b/utils/CMakeLists.txt
@@ -4,7 +4,7 @@ endif()
find_package(Boost REQUIRED COMPONENTS filesystem program_options system)
-set(block_util_headers block_cmd_parser.hpp block_dumper.hpp block_input.hpp data_parsers.hpp)
+set(block_util_headers block_cmd_parser.hpp block_dumper.hpp block_input.hpp data_parsers.hpp helpers/command_line.hpp)
add_executable(util_encrypt_block encrypt_block.cpp ${block_util_headers})
target_include_directories(util_encrypt_block SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
@@ -16,7 +16,7 @@ target_include_directories(util_decrypt_block SYSTEM PRIVATE ${Boost_INCLUDE_DIR
target_link_libraries(util_decrypt_block PRIVATE aesxx ${Boost_LIBRARIES})
set_target_properties(util_decrypt_block PROPERTIES OUTPUT_NAME decrypt_block)
-set(file_util_headers data_parsers.hpp file_cmd_parser.hpp helpers/file.hpp)
+set(file_util_headers data_parsers.hpp file_cmd_parser.hpp helpers/command_line.hpp helpers/file.hpp)
add_executable(util_encrypt_file encrypt_file.cpp ${file_util_headers})
target_include_directories(util_encrypt_file SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
diff --git a/utils/block_cmd_parser.hpp b/utils/block_cmd_parser.hpp
index 9c9d61c..58f86d8 100644
--- a/utils/block_cmd_parser.hpp
+++ b/utils/block_cmd_parser.hpp
@@ -7,157 +7,118 @@
#include "block_input.hpp"
#include "data_parsers.hpp"
+#include "helpers/command_line.hpp"
#include <aesxx/all.hpp>
-#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <deque>
#include <iterator>
-#include <ostream>
#include <string>
#include <utility>
#include <vector>
-namespace
+class BlockSettings : public command_line::SettingsParser
{
- class CommandLineParser;
+public:
+ aes::Algorithm algorithm = AES_AES128;
+ aes::Mode mode = AES_ECB;
- class Settings
- {
- public:
- aes::Algorithm algorithm = AES_AES128;
- aes::Mode mode = AES_ECB;
-
- bool use_boxes = false;
- bool verbose = false;
-
- private:
- Settings() = default;
+ bool use_boxes = false;
+ bool verbose = false;
- friend class CommandLineParser;
- };
+ std::vector<Input> inputs;
- class CommandLineParser
+ explicit BlockSettings(const std::string& argv0)
+ : SettingsParser{argv0}
{
- public:
- explicit CommandLineParser(const std::string& argv0)
- : prog_name{boost::filesystem::path{argv0}.filename().string()}
- , options{"Options"}
- { }
-
- Settings parse(int argc, char** argv, std::vector<Input>& inputs)
- {
- Settings settings;
-
- namespace po = boost::program_options;
-
- options.add_options()
- ("help,h", "show this message and exit")
- ("use-boxes,b", po::bool_switch(&settings.use_boxes)->default_value(false), "use the \"boxes\" interface")
- ("mode,m", po::value<aes::Mode>(&settings.mode)->required(), "set mode of operation")
- ("algorithm,a", po::value<aes::Algorithm>(&settings.algorithm)->required(), "set algorithm")
- ("verbose,v", po::bool_switch(&settings.verbose)->default_value(false), "enable verbose output");
+ visible.add_options()
+ ("verbose,v",
+ boost::program_options::bool_switch(&verbose),
+ "enable verbose output")
+ ("algorithm,a",
+ boost::program_options::value<aes::Algorithm>(&algorithm)
+ ->required()
+ ->value_name("NAME"),
+ "set algorithm")
+ ("mode,m",
+ boost::program_options::value<aes::Mode>(&mode)
+ ->required()
+ ->value_name("MODE"),
+ "set mode of operation")
+ ("use-boxes,b",
+ boost::program_options::bool_switch(&use_boxes),
+ "use the \"boxes\" interface");
+ hidden.add_options()
+ ("args",
+ boost::program_options::value<std::vector<std::string>>(&args),
+ "shouldn't be visible");
+ positional.add("args", -1);
+ }
- std::vector<std::string> args;
+ const char* get_short_description() const override
+ {
+ return "[-h|--help] [-v|--verbose] [-a|--algorithm NAME] [-m|--mode MODE]"
+ " [-- KEY [IV] [BLOCK]...]...";
+ }
- po::options_description hidden_options;
- hidden_options.add_options()
- ("positional", po::value<std::vector<std::string>>(&args));
+ void parse(int argc, char* argv[]) override
+ {
+ SettingsParser::parse(argc, argv);
+ parse_inputs(std::deque<std::string>{
+ std::make_move_iterator(args.begin()),
+ std::make_move_iterator(args.end())});
+ }
- po::options_description all_options;
- all_options.add(options).add(hidden_options);
+private:
+ void parse_inputs(std::deque<std::string>&& src)
+ {
+ while (!src.empty())
+ inputs.emplace_back(parse_input(src));
+ }
- po::positional_options_description positional_options;
- positional_options.add("positional", -1);
+ Input parse_input(std::deque<std::string>& src) const
+ {
+ std::string key{std::move(src.front())};
+ src.pop_front();
- po::variables_map vm;
- po::store(po::command_line_parser(argc, argv)
- .options(all_options)
- .positional(positional_options)
- .run(), vm);
+ std::string iv;
- if (vm.count("help"))
+ if (aes::mode_requires_init_vector(mode))
+ {
+ if (src.empty())
{
- help_flag = true;
- return settings;
+ throw boost::program_options::error{
+ "an initialization vector is required for the selected mode of operation"};
}
-
- po::notify(vm);
-
- inputs = parse_inputs(settings, std::deque<std::string>{
- std::make_move_iterator(args.begin()),
- std::make_move_iterator(args.end())});
-
- return settings;
- }
-
- bool exit_with_usage() const { return help_flag; }
-
- private:
- static std::vector<Input> parse_inputs(
- const Settings& settings,
- std::deque<std::string>&& args)
- {
- std::vector<Input> inputs;
- while (!args.empty())
- inputs.emplace_back(parse_input(settings, args));
- return inputs;
+ iv = std::move(src.front());
+ src.pop_front();
}
- static Input parse_input(
- const Settings& settings,
- std::deque<std::string>& args)
- {
- std::string key{std::move(args.front())};
- args.pop_front();
-
- std::string iv;
+ auto blocks = parse_blocks(src);
- if (aes::mode_requires_init_vector(settings.mode))
- {
- if (args.empty())
- throw boost::program_options::error{"an initialization vector is required for the selected mode of operation"};
- iv = std::move(args.front());
- args.pop_front();
- }
-
- auto blocks = parse_blocks(args);
+ if (aes::mode_requires_init_vector(mode))
+ return {key, iv, std::move(blocks)};
+ else
+ return {key, std::move(blocks)};
+ }
- if (aes::mode_requires_init_vector(settings.mode))
- return {key, iv, std::move(blocks)};
- else
- return {key, std::move(blocks)};
- }
+ static std::vector<std::string> parse_blocks(std::deque<std::string>& src)
+ {
+ std::vector<std::string> blocks;
- static std::vector<std::string> parse_blocks(std::deque<std::string>& args)
+ while (!src.empty())
{
- std::vector<std::string> blocks;
-
- while (!args.empty())
- {
- std::string block{std::move(args.front())};
- args.pop_front();
- if (block == "--")
- break;
- blocks.emplace_back(std::move(block));
- }
-
- return blocks;
+ std::string block{std::move(src.front())};
+ src.pop_front();
+ if (block == "--")
+ break;
+ blocks.emplace_back(std::move(block));
}
- const std::string prog_name;
- boost::program_options::options_description options;
-
- bool help_flag = false;
-
- friend std::ostream& operator<<(std::ostream&, const CommandLineParser&);
- };
-
- std::ostream& operator<<(std::ostream& os, const CommandLineParser& cmd_parser)
- {
- return os << "Usage: " << cmd_parser.prog_name << " [OPTIONS...] [-- KEY [IV] [BLOCK...]...]\n"
- << cmd_parser.options << "\n";
+ return blocks;
}
-}
+
+ std::vector<std::string> args;
+};
diff --git a/utils/decrypt_block.cpp b/utils/decrypt_block.cpp
index 34f8abe..1337d55 100644
--- a/utils/decrypt_block.cpp
+++ b/utils/decrypt_block.cpp
@@ -164,49 +164,48 @@ int main(int argc, char** argv)
{
try
{
- CommandLineParser cmd_parser{argv[0]};
+ BlockSettings settings{argv[0]};
+
try
{
- std::vector<Input> inputs;
- const auto settings = cmd_parser.parse(argc, argv, inputs);
-
- if (cmd_parser.exit_with_usage())
- {
- std::cout << cmd_parser;
- return 0;
- }
-
- for (const auto& input : inputs)
- {
- if (settings.use_boxes)
- {
- decrypt_using_boxes(
- settings.algorithm,
- settings.mode,
- input);
- }
- else
- {
- decrypt_using_cxx_api(
- settings.algorithm,
- settings.mode,
- input,
- settings.verbose);
- }
- }
+ settings.parse(argc, argv);
}
catch (const boost::program_options::error& e)
{
- std::cerr << "Usage error: " << e.what() << "\n";
- std::cerr << cmd_parser;
+ settings.usage_error(e);
return 1;
}
- catch (const aes::Error& e)
+
+ if (settings.exit_with_usage)
{
- std::cerr << e;
- return 1;
+ settings.usage();
+ return 0;
+ }
+
+ for (const auto& input : settings.inputs)
+ {
+ if (settings.use_boxes)
+ {
+ decrypt_using_boxes(
+ settings.algorithm,
+ settings.mode,
+ input);
+ }
+ else
+ {
+ decrypt_using_cxx_api(
+ settings.algorithm,
+ settings.mode,
+ input,
+ settings.verbose);
+ }
}
}
+ catch (const aes::Error& e)
+ {
+ std::cerr << e;
+ return 1;
+ }
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
diff --git a/utils/decrypt_bmp.cpp b/utils/decrypt_bmp.cpp
index a68b1d3..fc0b14a 100644
--- a/utils/decrypt_bmp.cpp
+++ b/utils/decrypt_bmp.cpp
@@ -31,7 +31,7 @@ namespace
file::write_file(plaintext_path, bmp.get_buffer(), bmp.get_size());
}
- void decrypt_bmp(const Settings& settings)
+ void decrypt_bmp(const FileSettings& settings)
{
const auto& algorithm = settings.algorithm;
const auto& mode = settings.mode;
@@ -59,30 +59,30 @@ int main(int argc, char** argv)
{
try
{
- CommandLineParser cmd_parser(argv[0]);
+ FileSettings settings{argv[0]};
+
try
{
- const auto settings = cmd_parser.parse(argc, argv);
-
- if (cmd_parser.exit_with_usage())
- {
- std::cout << cmd_parser;
- return 0;
- }
-
- decrypt_bmp(settings);
+ settings.parse(argc, argv);
}
catch (const boost::program_options::error& e)
{
- std::cerr << "Usage error: " << e.what() << "\n";
- std::cerr << cmd_parser;
+ settings.usage_error(e);
return 1;
}
- catch (const aes::Error& e)
+
+ if (settings.exit_with_usage)
{
- std::cerr << e;
- return 1;
+ settings.usage();
+ return 0;
}
+
+ decrypt_bmp(settings);
+ }
+ catch (const aes::Error& e)
+ {
+ std::cerr << e;
+ return 1;
}
catch (const std::exception& e)
{
diff --git a/utils/decrypt_file.cpp b/utils/decrypt_file.cpp
index 644447c..beaabff 100644
--- a/utils/decrypt_file.cpp
+++ b/utils/decrypt_file.cpp
@@ -28,7 +28,7 @@ namespace
file::write_file(plaintext_path, plaintext_buf);
}
- void decrypt_file(const Settings& settings)
+ void decrypt_file(const FileSettings& settings)
{
const auto& algorithm = settings.algorithm;
const auto& mode = settings.mode;
@@ -56,30 +56,30 @@ int main(int argc, char** argv)
{
try
{
- CommandLineParser cmd_parser{argv[0]};
+ FileSettings settings{argv[0]};
+
try
{
- const auto settings = cmd_parser.parse(argc, argv);
-
- if (cmd_parser.exit_with_usage())
- {
- std::cout << cmd_parser;
- return 0;
- }
-
- decrypt_file(settings);
+ settings.parse(argc, argv);
}
catch (const boost::program_options::error& e)
{
- std::cerr << "Usage error: " << e.what() << "\n";
- std::cerr << cmd_parser;
+ settings.usage_error(e);
return 1;
}
- catch (const aes::Error& e)
+
+ if (settings.exit_with_usage)
{
- std::cerr << e;
- return 1;
+ settings.usage();
+ return 0;
}
+
+ decrypt_file(settings);
+ }
+ catch (const aes::Error& e)
+ {
+ std::cerr << e;
+ return 1;
}
catch (const std::exception& e)
{
diff --git a/utils/encrypt_block.cpp b/utils/encrypt_block.cpp
index 5c02d92..b8cb55e 100644
--- a/utils/encrypt_block.cpp
+++ b/utils/encrypt_block.cpp
@@ -164,49 +164,48 @@ int main(int argc, char** argv)
{
try
{
- CommandLineParser cmd_parser{argv[0]};
+ BlockSettings settings{argv[0]};
+
try
{
- std::vector<Input> inputs;
- const auto settings = cmd_parser.parse(argc, argv, inputs);
-
- if (cmd_parser.exit_with_usage())
- {
- std::cout << cmd_parser;
- return 0;
- }
-
- for (const auto& input : inputs)
- {
- if (settings.use_boxes)
- {
- encrypt_using_boxes(
- settings.algorithm,
- settings.mode,
- input);
- }
- else
- {
- encrypt_using_cxx_api(
- settings.algorithm,
- settings.mode,
- input,
- settings.verbose);
- }
- }
+ settings.parse(argc, argv);
}
catch (const boost::program_options::error& e)
{
- std::cerr << "Usage error: " << e.what() << "\n";
- std::cerr << cmd_parser;
+ settings.usage_error(e);
return 1;
}
- catch (const aes::Error& e)
+
+ if (settings.exit_with_usage)
{
- std::cerr << e;
- return 1;
+ settings.usage();
+ return 0;
+ }
+
+ for (const auto& input : settings.inputs)
+ {
+ if (settings.use_boxes)
+ {
+ encrypt_using_boxes(
+ settings.algorithm,
+ settings.mode,
+ input);
+ }
+ else
+ {
+ encrypt_using_cxx_api(
+ settings.algorithm,
+ settings.mode,
+ input,
+ settings.verbose);
+ }
}
}
+ catch (const aes::Error& e)
+ {
+ std::cerr << e;
+ return 1;
+ }
catch (const std::exception& e)
{
std::cerr << e.what() << "\n";
diff --git a/utils/encrypt_bmp.cpp b/utils/encrypt_bmp.cpp
index ad61266..7f1a890 100644
--- a/utils/encrypt_bmp.cpp
+++ b/utils/encrypt_bmp.cpp
@@ -29,7 +29,7 @@ namespace
file::write_file(ciphertext_path, bmp.get_buffer(), bmp.get_size());
}
- void encrypt_bmp(const Settings& settings)
+ void encrypt_bmp(const FileSettings& settings)
{
const auto& algorithm = settings.algorithm;
const auto& mode = settings.mode;
@@ -57,30 +57,30 @@ int main(int argc, char** argv)
{
try
{
- CommandLineParser cmd_parser{argv[0]};
+ FileSettings settings{argv[0]};
+
try
{
- const auto settings = cmd_parser.parse(argc, argv);
-
- if (cmd_parser.exit_with_usage())
- {
- std::cout << cmd_parser;
- return 0;
- }
-
- encrypt_bmp(settings);
+ settings.parse(argc, argv);
}
catch (const boost::program_options::error& e)
{
- std::cerr << "Usage error: " << e.what() << "\n";
- std::cerr << cmd_parser;
+ settings.usage_error(e);
return 1;
}
- catch (const aes::Error& e)
+
+ if (settings.exit_with_usage)
{
- std::cerr << e;
- return 1;
+ settings.usage();
+ return 0;
}
+
+ encrypt_bmp(settings);
+ }
+ catch (const aes::Error& e)
+ {
+ std::cerr << e;
+ return 1;
}
catch (const std::exception& e)
{
diff --git a/utils/encrypt_file.cpp b/utils/encrypt_file.cpp
index 007ed18..c5fa41f 100644
--- a/utils/encrypt_file.cpp
+++ b/utils/encrypt_file.cpp
@@ -28,7 +28,7 @@ namespace
file::write_file(ciphertext_path, ciphertext_buf);
}
- void encrypt_file(const Settings& settings)
+ void encrypt_file(const FileSettings& settings)
{
const auto& algorithm = settings.algorithm;
const auto& mode = settings.mode;
@@ -56,30 +56,30 @@ int main(int argc, char** argv)
{
try
{
- CommandLineParser cmd_parser{argv[0]};
+ FileSettings settings{argv[0]};
+
try
{
- const auto settings = cmd_parser.parse(argc, argv);
-
- if (cmd_parser.exit_with_usage())
- {
- std::cout << cmd_parser;
- return 0;
- }
-
- encrypt_file(settings);
+ settings.parse(argc, argv);
}
catch (const boost::program_options::error& e)
{
- std::cerr << "Usage error: " << e.what() << "\n";
- std::cerr << cmd_parser;
+ settings.usage_error(e);
return 1;
}
- catch (const aes::Error& e)
+
+ if (settings.exit_with_usage)
{
- std::cerr << e;
- return 1;
+ settings.usage();
+ return 0;
}
+
+ encrypt_file(settings);
+ }
+ catch (const aes::Error& e)
+ {
+ std::cerr << e;
+ return 1;
}
catch (const std::exception& e)
{
diff --git a/utils/file_cmd_parser.hpp b/utils/file_cmd_parser.hpp
index e9cdaae..e199409 100644
--- a/utils/file_cmd_parser.hpp
+++ b/utils/file_cmd_parser.hpp
@@ -6,6 +6,7 @@
#pragma once
#include "data_parsers.hpp"
+#include "helpers/command_line.hpp"
#include <aesxx/all.hpp>
@@ -16,87 +17,67 @@
#include <string>
#include <utility>
-namespace
+class FileSettings : public command_line::SettingsParser
{
- class CommandLineParser;
+public:
+ aes::Algorithm algorithm = AES_AES128;
+ aes::Mode mode = AES_ECB;
- class Settings
- {
- public:
- aes::Algorithm algorithm = AES_AES128;
- aes::Mode mode = AES_ECB;
-
- std::string input_path;
- std::string output_path;
- std::string key;
- std::string iv;
+ std::string input_path;
+ std::string output_path;
+ std::string key;
+ std::string iv;
- private:
- Settings() = default;
+ explicit FileSettings(const std::string& argv0)
+ : SettingsParser{argv0}
+ {
+ visible.add_options()
+ ("algorithm,a",
+ boost::program_options::value<aes::Algorithm>(&algorithm)
+ ->required()
+ ->value_name("NAME"),
+ "set algorithm")
+ ("mode,m",
+ boost::program_options::value<aes::Mode>(&mode)
+ ->required()
+ ->value_name("MODE"),
+ "set mode of operation")
+ ("key,k",
+ boost::program_options::value<std::string>(&key)
+ ->required()
+ ->value_name("KEY"),
+ "set encryption key")
+ ("iv,v",
+ boost::program_options::value<std::string>(&iv)
+ ->value_name("BLOCK"),
+ "set initialization vector")
+ ("input,i",
+ boost::program_options::value<std::string>(&input_path)
+ ->required()
+ ->value_name("PATH"),
+ "set input file path")
+ ("output,o",
+ boost::program_options::value<std::string>(&output_path)
+ ->required()
+ ->value_name("PATH"),
+ "set output file path");
+ }
- friend class CommandLineParser;
- };
+ const char* get_short_description() const override
+ {
+ return "[-h|--help] [-a|--algorithm NAME] [-m|--mode MODE]"
+ " [-k|--key KEY] [-v|--iv BLOCK]"
+ " [-i|--input PATH] [-o|--output PATH]";
+ }
- class CommandLineParser
+ void parse(int argc, char** argv) override
{
- public:
- explicit CommandLineParser(const std::string& argv0)
- : prog_name{boost::filesystem::path{argv0}.filename().string()}
- , options{"Options"}
- { }
+ SettingsParser::parse(argc, argv);
- Settings parse(int argc, char** argv)
+ if (aes::mode_requires_init_vector(mode) && iv.empty())
{
- Settings settings;
-
- namespace po = boost::program_options;
-
- options.add_options()
- ("help,h", "show this message and exit")
- ("mode,m", po::value<aes::Mode>(&settings.mode)->required(), "set mode of operation")
- ("algorithm,a", po::value<aes::Algorithm>(&settings.algorithm)->required(), "set algorithm")
- ("input,i", po::value<std::string>(&settings.input_path)->required(), "set input file path")
- ("output,o", po::value<std::string>(&settings.output_path)->required(), "set output file path")
- ("key,k", po::value<std::string>(&settings.key)->required(), "set encryption key")
- ("iv,v", po::value<std::string>(&settings.iv), "set initialization vector");
-
- po::variables_map vm;
- po::store(po::parse_command_line(argc, argv, options), vm);
-
- if (vm.count("help"))
- {
- help_flag = true;
- return settings;
- }
-
- po::notify(vm);
-
- if (aes::mode_requires_init_vector(settings.mode))
- {
- if (!vm.count("iv"))
- {
- throw boost::program_options::error(
- "an initialization vector is required for the selected mode of operation");
- }
- }
-
- return settings;
+ throw boost::program_options::error{
+ "an initialization vector is required for the selected mode of operation"};
}
-
- bool exit_with_usage() const { return help_flag; }
-
- private:
- const std::string prog_name;
- boost::program_options::options_description options;
-
- bool help_flag = false;
-
- friend std::ostream& operator<<(std::ostream&, const CommandLineParser&);
- };
-
- std::ostream& operator<<(std::ostream& os, const CommandLineParser& cmd_parser)
- {
- return os << "Usage: " << cmd_parser.prog_name << " [OPTION...]\n"
- << cmd_parser.options << "\n";
}
-}
+};
diff --git a/utils/helpers/command_line.hpp b/utils/helpers/command_line.hpp
new file mode 100644
index 0000000..4843a06
--- /dev/null
+++ b/utils/helpers/command_line.hpp
@@ -0,0 +1,86 @@
+// Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "AES tools" project.
+// For details, see https://github.com/egor-tensin/aes-tools.
+// Distributed under the MIT License.
+
+#pragma once
+
+#include <boost/filesystem.hpp>
+#include <boost/program_options.hpp>
+
+#include <exception>
+#include <iostream>
+#include <ostream>
+#include <string>
+
+namespace command_line
+{
+ class SettingsParser
+ {
+ public:
+ explicit SettingsParser(const std::string& argv0)
+ : prog_name{extract_filename(argv0)}
+ {
+ visible.add_options()
+ ("help,h",
+ "show this message and exit");
+ }
+
+ virtual ~SettingsParser() = default;
+
+ virtual const char* get_short_description() const
+ {
+ return "[--option VALUE]...";
+ }
+
+ virtual void parse(int argc, char* argv[])
+ {
+ boost::program_options::options_description all;
+ all.add(hidden).add(visible);
+ boost::program_options::variables_map vm;
+ boost::program_options::store(
+ boost::program_options::command_line_parser{argc, argv}
+ .options(all)
+ .positional(positional)
+ .run(),
+ vm);
+ if (vm.count("help"))
+ exit_with_usage = true;
+ else
+ boost::program_options::notify(vm);
+ }
+
+ bool exit_with_usage = false;
+
+ void usage() const
+ {
+ std::cout << *this;
+ }
+
+ void usage_error(const std::exception& e) const
+ {
+ std::cerr << "usage error: " << e.what() << '\n';
+ std::cerr << *this;
+ }
+
+ protected:
+ boost::program_options::options_description hidden;
+ boost::program_options::options_description visible;
+ boost::program_options::positional_options_description positional;
+
+ static std::string extract_filename(const std::string& path)
+ {
+ return boost::filesystem::path{path}.filename().string();
+ }
+
+ const std::string prog_name;
+
+ friend std::ostream& operator<<(std::ostream& os, const SettingsParser& parser)
+ {
+ const auto short_descr = parser.get_short_description();
+ os << "usage: " << parser.prog_name << ' ' << short_descr << '\n';
+ os << parser.visible;
+ return os;
+ }
+ };
+}