aboutsummaryrefslogtreecommitdiffstats
path: root/src/cli/cli.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli/cli.cpp')
-rw-r--r--src/cli/cli.cpp250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp
new file mode 100644
index 000000000..f23c3574d
--- /dev/null
+++ b/src/cli/cli.cpp
@@ -0,0 +1,250 @@
+/*
+* (C) 2015,2017 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "cli.h"
+#include "argparse.h"
+#include <botan/rng.h>
+#include <botan/parsing.h>
+#include <iostream>
+#include <fstream>
+
+namespace Botan_CLI {
+
+Command::Command(const std::string& cmd_spec) : m_spec(cmd_spec)
+ {
+ // for checking all spec strings at load time
+ //m_args.reset(new Argument_Parser(m_spec));
+ }
+
+Command::~Command() { /* for unique_ptr */ }
+
+std::string Command::help_text() const
+ {
+ return "Usage: " + m_spec +
+ "\n\nAll commands support --verbose --help --output= --error-output= --rng-type= --drbg-seed=";
+ }
+
+int Command::run(const std::vector<std::string>& params)
+ {
+ try
+ {
+ m_args.reset(new Argument_Parser(m_spec,
+ {"verbose", "help"},
+ {"output", "error-output", "rng-type", "drbg-seed"}));
+
+ m_args->parse_args(params);
+
+ if(m_args->has_arg("output"))
+ {
+ const std::string output_file = get_arg("output");
+
+ if(output_file != "")
+ {
+ m_output_stream.reset(new std::ofstream(output_file, std::ios::binary));
+ if(!m_output_stream->good())
+ throw CLI_IO_Error("opening", output_file);
+ }
+ }
+
+ if(m_args->has_arg("error-output"))
+ {
+ const std::string output_file = get_arg("error-output");
+
+ if(output_file != "")
+ {
+ m_error_output_stream.reset(new std::ofstream(output_file, std::ios::binary));
+ if(!m_error_output_stream->good())
+ throw CLI_IO_Error("opening", output_file);
+ }
+ }
+
+ if(flag_set("help"))
+ {
+ output() << help_text() << "\n";
+ return 2;
+ }
+
+ this->go();
+ return 0;
+ }
+ catch(CLI_Usage_Error& e)
+ {
+ error_output() << "Usage error: " << e.what() << "\n";
+ error_output() << help_text() << "\n";
+ return 1;
+ }
+ catch(std::exception& e)
+ {
+ error_output() << "Error: " << e.what() << "\n";
+ return 2;
+ }
+ catch(...)
+ {
+ error_output() << "Error: unknown exception\n";
+ return 2;
+ }
+ }
+
+bool Command::flag_set(const std::string& flag_name) const
+ {
+ return m_args->flag_set(flag_name);
+ }
+
+std::string Command::get_arg(const std::string& opt_name) const
+ {
+ return m_args->get_arg(opt_name);
+ }
+
+/*
+* Like get_arg() but if the argument was not specified or is empty, returns otherwise
+*/
+std::string Command::get_arg_or(const std::string& opt_name, const std::string& otherwise) const
+ {
+ return m_args->get_arg_or(opt_name, otherwise);
+ }
+
+size_t Command::get_arg_sz(const std::string& opt_name) const
+ {
+ return m_args->get_arg_sz(opt_name);
+ }
+
+std::vector<std::string> Command::get_arg_list(const std::string& what) const
+ {
+ return m_args->get_arg_list(what);
+ }
+
+std::ostream& Command::output()
+ {
+ if(m_output_stream.get())
+ {
+ return *m_output_stream;
+ }
+ return std::cout;
+ }
+
+std::ostream& Command::error_output()
+ {
+ if(m_error_output_stream.get())
+ {
+ return *m_error_output_stream;
+ }
+ return std::cerr;
+ }
+
+std::vector<uint8_t> Command::slurp_file(const std::string& input_file,
+ size_t buf_size) const
+ {
+ std::vector<uint8_t> buf;
+ auto insert_fn = [&](const uint8_t b[], size_t l)
+ {
+ buf.insert(buf.end(), b, b + l);
+ };
+ this->read_file(input_file, insert_fn, buf_size);
+ return buf;
+ }
+
+std::string Command::slurp_file_as_str(const std::string& input_file,
+ size_t buf_size) const
+ {
+ std::string str;
+ auto insert_fn = [&](const uint8_t b[], size_t l)
+ {
+ str.append(reinterpret_cast<const char*>(b), l);
+ };
+ this->read_file(input_file, insert_fn, buf_size);
+ return str;
+ }
+
+void Command::read_file(const std::string& input_file,
+ std::function<void (uint8_t[], size_t)> consumer_fn,
+ size_t buf_size) const
+ {
+ if(input_file == "-")
+ {
+ do_read_file(std::cin, consumer_fn, buf_size);
+ }
+ else
+ {
+ std::ifstream in(input_file, std::ios::binary);
+ if(!in)
+ {
+ throw CLI_IO_Error("reading file", input_file);
+ }
+ do_read_file(in, consumer_fn, buf_size);
+ }
+ }
+
+void Command::do_read_file(std::istream& in,
+ std::function<void (uint8_t[], size_t)> consumer_fn,
+ size_t buf_size) const
+ {
+ // Avoid an infinite loop on --buf-size=0
+ std::vector<uint8_t> buf(buf_size == 0 ? 4096 : buf_size);
+
+ while(in.good())
+ {
+ in.read(reinterpret_cast<char*>(buf.data()), buf.size());
+ const size_t got = static_cast<size_t>(in.gcount());
+ consumer_fn(buf.data(), got);
+ }
+ }
+
+Botan::RandomNumberGenerator& Command::rng()
+ {
+ if(m_rng == nullptr)
+ {
+ m_rng = cli_make_rng(get_arg("rng-type"), get_arg("drbg-seed"));
+ }
+
+ return *m_rng.get();
+ }
+
+// Registration code
+
+Command::Registration::Registration(const std::string& name, Command::cmd_maker_fn maker_fn)
+ {
+ std::map<std::string, Command::cmd_maker_fn>& reg = Command::global_registry();
+
+ if(reg.count(name) > 0)
+ {
+ throw CLI_Error("Duplicated registration of command " + name);
+ }
+
+ reg.insert(std::make_pair(name, maker_fn));
+ }
+
+//static
+std::map<std::string, Command::cmd_maker_fn>& Command::global_registry()
+ {
+ static std::map<std::string, Command::cmd_maker_fn> g_cmds;
+ return g_cmds;
+ }
+
+//static
+std::vector<std::string> Command::registered_cmds()
+ {
+ std::vector<std::string> cmds;
+ for(auto& cmd : Command::global_registry())
+ cmds.push_back(cmd.first);
+ return cmds;
+ }
+
+//static
+std::unique_ptr<Command> Command::get_cmd(const std::string& name)
+ {
+ const std::map<std::string, Command::cmd_maker_fn>& reg = Command::global_registry();
+
+ std::unique_ptr<Command> r;
+ auto i = reg.find(name);
+ if(i != reg.end())
+ {
+ r.reset(i->second());
+ }
+
+ return r;
+ }
+
+}