/* * (C) 2015 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #ifndef BOTAN_CLI_H_ #define BOTAN_CLI_H_ #include #include #include #include #include #include #include #include "cli_exceptions.h" namespace Botan { class RandomNumberGenerator; } namespace Botan_CLI { class Argument_Parser; /* Declared in cli_rng.cpp */ std::unique_ptr cli_make_rng(const std::string& type, const std::string& hex_drbg_seed); class Command { public: /** * Get a registered command */ static std::unique_ptr get_cmd(const std::string& name); static std::vector registered_cmds(); /** * The spec string specifies the format of the command line, eg for * a somewhat complicated command: * cmd_name --flag --option1= --option2=opt2val input1 input2 *rest * * By default this is the value returned by help_text() * * The first value is always the command name. Options may appear * in any order. Named arguments are taken from the command line * in the order they appear in the spec. * * --flag can optionally be specified, and takes no value. * Check for it in go() with flag_set() * * --option1 is an option whose default value (if the option * does not appear on the command line) is the empty string. * * --option2 is an option whose default value is opt2val * Read the values in go() using get_arg or get_arg_sz. * * The values input1 and input2 specify named arguments which must * be provided. They are also access via get_arg/get_arg_sz * Because options and arguments for a single command share the same * namespace you can't have a spec like: * cmd --input input * but you hopefully didn't want to do that anyway. * * The leading '*' on '*rest' specifies that all remaining arguments * should be packaged in a list which is available as get_arg_list("rest"). * This can only appear on a single value and should be the final * named argument. * * Every command has implicit flags --help, --verbose and implicit * options --output= and --error-output= which override the default * use of std::cout and std::cerr. * * Use of --help is captured in run() and returns help_text(). * Use of --verbose can be checked with verbose() or flag_set("verbose") */ explicit Command(const std::string& cmd_spec); virtual ~Command(); int run(const std::vector& params); virtual std::string group() const = 0; virtual std::string description() const = 0; virtual std::string help_text() const; const std::string& cmd_spec() const { return m_spec; } std::string cmd_name() const; protected: /* * The actual functionality of the cli command implemented in subclass. * The return value from main will be zero. */ virtual void go() = 0; void set_return_code(int rc) { m_return_code = rc; } std::ostream& output(); std::ostream& error_output(); bool verbose() const { return flag_set("verbose"); } std::string get_passphrase(const std::string& prompt); bool flag_set(const std::string& flag_name) const; static std::string format_blob(const std::string& format, const uint8_t bits[], size_t len); template static std::string format_blob(const std::string& format, const std::vector& vec) { return format_blob(format, vec.data(), vec.size()); } std::string get_arg(const std::string& opt_name) const; /** * Like get_arg but if the value is '-' then reads a passphrase from * the terminal with echo suppressed. */ std::string get_passphrase_arg(const std::string& prompt, const std::string& opt_name); /* * Like get_arg() but if the argument was not specified or is empty, returns otherwise */ std::string get_arg_or(const std::string& opt_name, const std::string& otherwise) const; size_t get_arg_sz(const std::string& opt_name) const; uint16_t get_arg_u16(const std::string& opt_name) const; uint32_t get_arg_u32(const std::string& opt_name) const; std::vector get_arg_list(const std::string& what) const; /* * Read an entire file into memory and return the contents */ std::vector slurp_file(const std::string& input_file, size_t buf_size = 0) const; std::string slurp_file_as_str(const std::string& input_file, size_t buf_size = 0) const; /* * Read a file calling consumer_fn() with the inputs */ void read_file(const std::string& input_file, std::function consumer_fn, size_t buf_size = 0) const; void do_read_file(std::istream& in, std::function consumer_fn, size_t buf_size = 0) const; template void write_output(const std::vector& vec) { output().write(reinterpret_cast(vec.data()), vec.size()); } Botan::RandomNumberGenerator& rng(); private: typedef std::function cmd_maker_fn; static std::map& global_registry(); void parse_spec(); // set in constructor std::string m_spec; std::unique_ptr m_args; std::unique_ptr m_output_stream; std::unique_ptr m_error_output_stream; std::unique_ptr m_rng; // possibly set by calling set_return_code() int m_return_code = 0; public: // the registry interface: class Registration final { public: Registration(const std::string& name, cmd_maker_fn maker_fn); }; }; #define BOTAN_REGISTER_COMMAND(name, CLI_Class) \ Botan_CLI::Command::Registration reg_cmd_ ## CLI_Class(name, \ []() -> Botan_CLI::Command* { return new CLI_Class; }) } #endif