• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 5111374265

29 May 2023 11:19AM UTC coverage: 92.227% (+0.5%) from 91.723%
5111374265

push

github

randombit
Next release will be 3.1.0. Update release notes

75588 of 81959 relevant lines covered (92.23%)

11886470.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.27
/src/cli/cli.cpp
1
/*
2
* (C) 2015,2017 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6

7
#include "cli.h"
8
#include "argparse.h"
9
#include <botan/rng.h>
10
#include <botan/internal/os_utils.h>
11
#include <fstream>
12
#include <iostream>
13

14
#if defined(BOTAN_HAS_HEX_CODEC)
15
   #include <botan/hex.h>
16
#endif
17

18
#if defined(BOTAN_HAS_BASE64_CODEC)
19
   #include <botan/base64.h>
20
#endif
21

22
#if defined(BOTAN_HAS_BASE58_CODEC)
23
   #include <botan/base58.h>
24
#endif
25

26
namespace Botan_CLI {
27

28
Command::Command(const std::string& cmd_spec) : m_spec(cmd_spec) {
6,641✔
29
   // for checking all spec strings at load time
30
   //m_args.reset(new Argument_Parser(m_spec));
31
}
6,641✔
32

33
Command::~Command() = default;
6,829✔
34

35
std::string Command::cmd_name() const { return m_spec.substr(0, m_spec.find(' ')); }
71✔
36

37
std::string Command::help_text() const { return "Usage: " + m_spec; }
3✔
38

39
//static
40
std::vector<std::string> Command::split_on(const std::string& str, char delim) {
135✔
41
   return Argument_Parser::split_on(str, delim);
135✔
42
}
43

44
int Command::run(const std::vector<std::string>& params) {
6,569✔
45
   try {
6,569✔
46
      const std::vector<std::string> extra_flags = {"verbose", "help"};
19,707✔
47
      const std::vector<std::string> extra_opts = {"output", "error-output", "rng-type", "drbg-seed"};
32,845✔
48

49
      m_args = std::make_unique<Argument_Parser>(m_spec, extra_flags, extra_opts);
6,569✔
50

51
      m_args->parse_args(params);
6,569✔
52

53
      if(m_args->has_arg("output")) {
13,138✔
54
         const std::string output_file = get_arg("output");
13,138✔
55

56
         if(!output_file.empty()) {
6,569✔
57
            m_output_stream = std::make_unique<std::ofstream>(output_file, std::ios::binary);
74✔
58
            if(!m_output_stream->good())
74✔
59
               throw CLI_IO_Error("opening", output_file);
×
60
         }
61
      }
6,569✔
62

63
      if(m_args->has_arg("error-output")) {
13,138✔
64
         const std::string output_file = get_arg("error-output");
13,138✔
65

66
         if(!output_file.empty()) {
6,569✔
67
            m_error_output_stream = std::make_unique<std::ofstream>(output_file, std::ios::binary);
×
68
            if(!m_error_output_stream->good())
×
69
               throw CLI_IO_Error("opening", output_file);
×
70
         }
71
      }
6,569✔
72

73
      if(flag_set("help")) {
13,138✔
74
         output() << help_text() << "\n";
×
75
         return 2;
×
76
      }
77

78
      this->go();
6,569✔
79
      return m_return_code;
6,564✔
80
   } catch(CLI_Usage_Error& e) {
6,579✔
81
      error_output() << "Usage error: " << e.what() << "\n";
3✔
82
      error_output() << help_text() << "\n";
6✔
83
      return 1;
3✔
84
   } catch(std::exception& e) {
5✔
85
      error_output() << "Error: " << e.what() << "\n";
2✔
86
      return 2;
2✔
87
   } catch(...) {
2✔
88
      error_output() << "Error: unknown exception\n";
×
89
      return 2;
×
90
   }
×
91
}
92

93
bool Command::flag_set(const std::string& flag_name) const { return m_args->flag_set(flag_name); }
13,352✔
94

95
std::string Command::get_arg(const std::string& opt_name) const { return m_args->get_arg(opt_name); }
26,754✔
96

97
/*
98
* Like get_arg() but if the argument was not specified or is empty, returns otherwise
99
*/
100
std::string Command::get_arg_or(const std::string& opt_name, const std::string& otherwise) const {
18,844✔
101
   return m_args->get_arg_or(opt_name, otherwise);
18,844✔
102
}
103

104
std::optional<std::string> Command::get_arg_maybe(const std::string& opt_name) const {
18✔
105
   auto arg = m_args->get_arg(opt_name);
18✔
106
   if(arg.empty())
18✔
107
      return std::nullopt;
14✔
108
   else
109
      return arg;
22✔
110
}
18✔
111

112
size_t Command::get_arg_sz(const std::string& opt_name) const { return m_args->get_arg_sz(opt_name); }
6,506✔
113

114
uint16_t Command::get_arg_u16(const std::string& opt_name) const {
7✔
115
   const size_t val = get_arg_sz(opt_name);
7✔
116
   if(static_cast<uint16_t>(val) != val)
7✔
117
      throw CLI_Usage_Error("Argument " + opt_name + " has value out of allowed range");
×
118
   return static_cast<uint16_t>(val);
7✔
119
}
120

121
uint32_t Command::get_arg_u32(const std::string& opt_name) const {
×
122
   const size_t val = get_arg_sz(opt_name);
×
123
   if(static_cast<uint32_t>(val) != val)
×
124
      throw CLI_Usage_Error("Argument " + opt_name + " has value out of allowed range");
×
125
   return static_cast<uint32_t>(val);
×
126
}
127

128
std::vector<std::string> Command::get_arg_list(const std::string& what) const { return m_args->get_arg_list(what); }
99✔
129

130
std::ostream& Command::output() {
8,361✔
131
   if(m_output_stream) {
8,361✔
132
      return *m_output_stream;
319✔
133
   }
134
   return std::cout;
135
}
136

137
std::ostream& Command::error_output() {
13✔
138
   if(m_error_output_stream) {
13✔
139
      return *m_error_output_stream;
×
140
   }
141
   return std::cerr;
142
}
143

144
std::vector<uint8_t> Command::slurp_file(const std::string& input_file, size_t buf_size) const {
6,312✔
145
   std::vector<uint8_t> buf;
6,312✔
146
   auto insert_fn = [&](const uint8_t b[], size_t l) { buf.insert(buf.end(), b, b + l); };
6,316✔
147
   Command::read_file(input_file, insert_fn, buf_size);
6,312✔
148
   return buf;
6,312✔
149
}
×
150

151
std::string Command::slurp_file_as_str(const std::string& input_file, size_t buf_size) const {
17✔
152
   std::string str;
17✔
153
   auto insert_fn = [&](const uint8_t b[], size_t l) { str.append(reinterpret_cast<const char*>(b), l); };
19✔
154
   Command::read_file(input_file, insert_fn, buf_size);
19✔
155
   return str;
15✔
156
}
2✔
157

158
void Command::read_file(const std::string& input_file,
6,367✔
159
                        const std::function<void(uint8_t[], size_t)>& consumer_fn,
160
                        size_t buf_size) {
161
   if(input_file == "-") {
6,367✔
162
      do_read_file(std::cin, consumer_fn, buf_size);
6,293✔
163
   } else {
164
      std::ifstream in(input_file, std::ios::binary);
74✔
165
      if(!in) {
74✔
166
         throw CLI_IO_Error("reading file", input_file);
4✔
167
      }
168
      do_read_file(in, consumer_fn, buf_size);
72✔
169
   }
74✔
170
}
6,365✔
171

172
//static
173
void Command::do_read_file(std::istream& in,
6,365✔
174
                           const std::function<void(uint8_t[], size_t)>& consumer_fn,
175
                           size_t buf_size) {
176
   // Avoid an infinite loop on --buf-size=0
177
   std::vector<uint8_t> buf(buf_size == 0 ? 4096 : buf_size);
6,427✔
178

179
   while(in.good()) {
12,753✔
180
      in.read(reinterpret_cast<char*>(buf.data()), buf.size());
6,388✔
181
      const size_t got = static_cast<size_t>(in.gcount());
6,388✔
182
      consumer_fn(buf.data(), got);
12,776✔
183
   }
184
}
6,365✔
185

186
Botan::RandomNumberGenerator& Command::rng() { return *rng_as_shared(); }
5,824✔
187

188
std::shared_ptr<Botan::RandomNumberGenerator> Command::rng_as_shared() {
5,839✔
189
   if(m_rng == nullptr) {
5,839✔
190
      m_rng = cli_make_rng(get_arg("rng-type"), get_arg("drbg-seed"));
449✔
191
   }
192

193
   return m_rng;
5,839✔
194
}
195

196
std::string Command::get_passphrase_arg(const std::string& prompt, const std::string& opt_name) {
74✔
197
   std::string s = get_arg(opt_name);
74✔
198
   if(s != "-")
74✔
199
      return s;
148✔
200
   return get_passphrase(prompt);
×
201
}
74✔
202

203
namespace {
204

205
bool echo_suppression_supported() {
×
206
   auto echo = Botan::OS::suppress_echo_on_terminal();
×
207
   return (echo != nullptr);
×
208
}
×
209

210
}  // namespace
211

212
std::string Command::get_passphrase(const std::string& prompt) {
×
213
   if(echo_suppression_supported() == false)
×
214
      error_output() << "Warning: terminal echo suppression not enabled for this platform\n";
×
215

216
   error_output() << prompt << ": " << std::flush;
×
217
   std::string pass;
×
218

219
   auto echo_suppress = Botan::OS::suppress_echo_on_terminal();
×
220

221
   std::getline(std::cin, pass);
×
222

223
   return pass;
×
224
}
×
225

226
//static
227
std::string Command::format_blob(const std::string& format, const uint8_t bits[], size_t len) {
38✔
228
#if defined(BOTAN_HAS_HEX_CODEC)
229
   if(format == "hex") {
38✔
230
      return Botan::hex_encode(bits, len);
32✔
231
   }
232
#endif
233

234
#if defined(BOTAN_HAS_BASE64_CODEC)
235
   if(format == "base64") {
6✔
236
      return Botan::base64_encode(bits, len);
2✔
237
   }
238
#endif
239

240
#if defined(BOTAN_HAS_BASE58_CODEC)
241
   if(format == "base58") {
4✔
242
      return Botan::base58_encode(bits, len);
2✔
243
   }
244
   if(format == "base58check") {
2✔
245
      return Botan::base58_check_encode(bits, len);
2✔
246
   }
247
#endif
248

249
   // If we supported format, we would have already returned
250
   throw CLI_Usage_Error("Unknown or unsupported format type");
×
251
}
252

253
// Registration code
254

255
Command::Registration::Registration(const std::string& name, const Command::cmd_maker_fn& maker_fn) {
472,968✔
256
   std::map<std::string, Command::cmd_maker_fn>& reg = Command::global_registry();
472,968✔
257

258
   if(reg.contains(name)) {
472,968✔
259
      throw CLI_Error("Duplicated registration of command " + name);
×
260
   }
261

262
   reg.insert(std::make_pair(name, maker_fn));
472,968✔
263
}
472,968✔
264

265
//static
266
std::map<std::string, Command::cmd_maker_fn>& Command::global_registry() {
479,619✔
267
   static std::map<std::string, Command::cmd_maker_fn> g_cmds;
479,619✔
268
   return g_cmds;
479,619✔
269
}
270

271
//static
272
std::vector<std::string> Command::registered_cmds() {
10✔
273
   std::vector<std::string> cmds;
10✔
274
   for(auto& cmd : Command::global_registry())
730✔
275
      cmds.push_back(cmd.first);
720✔
276
   return cmds;
10✔
277
}
×
278

279
//static
280
std::unique_ptr<Command> Command::get_cmd(const std::string& name) {
6,641✔
281
   const auto& reg = Command::global_registry();
6,641✔
282

283
   auto i = reg.find(name);
6,641✔
284
   if(i != reg.end()) {
6,641✔
285
      return i->second();
6,641✔
286
   }
287

288
   return nullptr;
×
289
}
290

291
}  // namespace Botan_CLI
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc