Source code

Revision control

Copy as Markdown

Other Tools

/*
* (C) 2015 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
#ifndef BOTAN_CLI_H_
#define BOTAN_CLI_H_
#include <botan/build.h>
#include <functional>
#include <ostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "cli_exceptions.h"
namespace Botan {
class RandomNumberGenerator;
}
namespace Botan_CLI {
class Argument_Parser;
/* Declared in cli_rng.cpp */
std::unique_ptr<Botan::RandomNumberGenerator>
cli_make_rng(const std::string& type = "", const std::string& hex_drbg_seed = "");
class Command
{
public:
/**
* Get a registered command
*/
static std::unique_ptr<Command> get_cmd(const std::string& name);
static std::vector<std::string> 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<std::string>& 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<typename Alloc>
static std::string format_blob(const std::string& format,
const std::vector<uint8_t, Alloc>& 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<std::string> get_arg_list(const std::string& what) const;
/*
* Read an entire file into memory and return the contents
*/
std::vector<uint8_t> 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<void (uint8_t[], size_t)> consumer_fn,
size_t buf_size = 0) const;
void do_read_file(std::istream& in,
std::function<void (uint8_t[], size_t)> consumer_fn,
size_t buf_size = 0) const;
template<typename Alloc>
void write_output(const std::vector<uint8_t, Alloc>& vec)
{
output().write(reinterpret_cast<const char*>(vec.data()), vec.size());
}
Botan::RandomNumberGenerator& rng();
private:
typedef std::function<Command* ()> cmd_maker_fn;
static std::map<std::string, cmd_maker_fn>& global_registry();
void parse_spec();
// set in constructor
std::string m_spec;
std::unique_ptr<Argument_Parser> m_args;
std::unique_ptr<std::ostream> m_output_stream;
std::unique_ptr<std::ostream> m_error_output_stream;
std::unique_ptr<Botan::RandomNumberGenerator> 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