aboutsummaryrefslogtreecommitdiffstats
path: root/src/cli/cli.h
blob: 26ead9081c5a557c43befc467b77f96f816e8770 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*
* (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");
         }

      bool flag_set(const std::string& flag_name) const;

      std::string get_arg(const std::string& opt_name) const;

      /*
      * 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;

      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:
      void parse_spec();

      typedef std::function<Command* ()> cmd_maker_fn;

      static std::map<std::string, cmd_maker_fn>& global_registry();

      // 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