diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2017-06-20 18:22:00 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2017-06-22 01:38:49 +0300 |
commit | 06b2428c2afaa36997a9d341bd497d42aa0fef89 (patch) | |
tree | ff83476738222578a19b59419fdbaa79a05156d7 | |
parent | README update (diff) | |
download | aes-tools-06b2428c2afaa36997a9d341bd497d42aa0fef89.tar.gz aes-tools-06b2428c2afaa36997a9d341bd497d42aa0fef89.zip |
utils: refactor command-line arguments parsing
+ update usage messages.
-rw-r--r-- | utils/CMakeLists.txt | 4 | ||||
-rw-r--r-- | utils/block_cmd_parser.hpp | 203 | ||||
-rw-r--r-- | utils/decrypt_block.cpp | 65 | ||||
-rw-r--r-- | utils/decrypt_bmp.cpp | 32 | ||||
-rw-r--r-- | utils/decrypt_file.cpp | 32 | ||||
-rw-r--r-- | utils/encrypt_block.cpp | 65 | ||||
-rw-r--r-- | utils/encrypt_bmp.cpp | 32 | ||||
-rw-r--r-- | utils/encrypt_file.cpp | 32 | ||||
-rw-r--r-- | utils/file_cmd_parser.hpp | 129 | ||||
-rw-r--r-- | utils/helpers/command_line.hpp | 86 |
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; + } + }; +} |