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

randombit / botan / 5230455705

10 Jun 2023 02:30PM UTC coverage: 91.715% (-0.03%) from 91.746%
5230455705

push

github

randombit
Merge GH #3584 Change clang-format AllowShortFunctionsOnASingleLine config from All to Inline

77182 of 84154 relevant lines covered (91.72%)

11975295.43 hits per line

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

78.26
/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,648✔
29
   // for checking all spec strings at load time
30
   //m_args.reset(new Argument_Parser(m_spec));
31
}
6,648✔
32

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

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

39
std::string Command::help_text() const {
3✔
40
   return "Usage: " + m_spec;
3✔
41
}
42

43
//static
44
std::vector<std::string> Command::split_on(const std::string& str, char delim) {
138✔
45
   return Argument_Parser::split_on(str, delim);
138✔
46
}
47

48
int Command::run(const std::vector<std::string>& params) {
6,576✔
49
   try {
6,576✔
50
      const std::vector<std::string> extra_flags = {"verbose", "help"};
19,728✔
51
      const std::vector<std::string> extra_opts = {"output", "error-output", "rng-type", "drbg-seed"};
32,880✔
52

53
      m_args = std::make_unique<Argument_Parser>(m_spec, extra_flags, extra_opts);
6,576✔
54

55
      m_args->parse_args(params);
6,576✔
56

57
      if(m_args->has_arg("output")) {
13,152✔
58
         const std::string output_file = get_arg("output");
13,152✔
59

60
         if(!output_file.empty()) {
6,576✔
61
            m_output_stream = std::make_unique<std::ofstream>(output_file, std::ios::binary);
77✔
62
            if(!m_output_stream->good()) {
77✔
63
               throw CLI_IO_Error("opening", output_file);
×
64
            }
65
         }
66
      }
6,576✔
67

68
      if(m_args->has_arg("error-output")) {
13,152✔
69
         const std::string output_file = get_arg("error-output");
13,152✔
70

71
         if(!output_file.empty()) {
6,576✔
72
            m_error_output_stream = std::make_unique<std::ofstream>(output_file, std::ios::binary);
×
73
            if(!m_error_output_stream->good()) {
×
74
               throw CLI_IO_Error("opening", output_file);
×
75
            }
76
         }
77
      }
6,576✔
78

79
      if(flag_set("help")) {
13,152✔
80
         output() << help_text() << "\n";
×
81
         return 2;
×
82
      }
83

84
      this->go();
6,576✔
85
      return m_return_code;
6,571✔
86
   } catch(CLI_Usage_Error& e) {
6,586✔
87
      error_output() << "Usage error: " << e.what() << "\n";
3✔
88
      error_output() << help_text() << "\n";
6✔
89
      return 1;
3✔
90
   } catch(std::exception& e) {
5✔
91
      error_output() << "Error: " << e.what() << "\n";
2✔
92
      return 2;
2✔
93
   } catch(...) {
2✔
94
      error_output() << "Error: unknown exception\n";
×
95
      return 2;
×
96
   }
×
97
}
98

99
bool Command::flag_set(const std::string& flag_name) const {
13,363✔
100
   return m_args->flag_set(flag_name);
13,363✔
101
}
102

103
std::string Command::get_arg(const std::string& opt_name) const {
26,880✔
104
   return m_args->get_arg(opt_name);
26,791✔
105
}
106

107
/*
108
* Like get_arg() but if the argument was not specified or is empty, returns otherwise
109
*/
110
std::string Command::get_arg_or(const std::string& opt_name, const std::string& otherwise) const {
18,844✔
111
   return m_args->get_arg_or(opt_name, otherwise);
18,844✔
112
}
113

114
std::optional<std::string> Command::get_arg_maybe(const std::string& opt_name) const {
18✔
115
   auto arg = m_args->get_arg(opt_name);
18✔
116
   if(arg.empty()) {
18✔
117
      return std::nullopt;
14✔
118
   } else {
119
      return arg;
22✔
120
   }
121
}
18✔
122

123
size_t Command::get_arg_sz(const std::string& opt_name) const {
6,520✔
124
   return m_args->get_arg_sz(opt_name);
6,513✔
125
}
126

127
uint16_t Command::get_arg_u16(const std::string& opt_name) const {
7✔
128
   const size_t val = get_arg_sz(opt_name);
7✔
129
   if(static_cast<uint16_t>(val) != val) {
7✔
130
      throw CLI_Usage_Error("Argument " + opt_name + " has value out of allowed range");
×
131
   }
132
   return static_cast<uint16_t>(val);
7✔
133
}
134

135
uint32_t Command::get_arg_u32(const std::string& opt_name) const {
×
136
   const size_t val = get_arg_sz(opt_name);
×
137
   if(static_cast<uint32_t>(val) != val) {
×
138
      throw CLI_Usage_Error("Argument " + opt_name + " has value out of allowed range");
×
139
   }
140
   return static_cast<uint32_t>(val);
×
141
}
142

143
std::vector<std::string> Command::get_arg_list(const std::string& what) const {
103✔
144
   return m_args->get_arg_list(what);
103✔
145
}
146

147
std::ostream& Command::output() {
8,364✔
148
   if(m_output_stream) {
8,364✔
149
      return *m_output_stream;
322✔
150
   }
151
   return std::cout;
152
}
153

154
std::ostream& Command::error_output() {
13✔
155
   if(m_error_output_stream) {
13✔
156
      return *m_error_output_stream;
×
157
   }
158
   return std::cerr;
159
}
160

161
std::vector<uint8_t> Command::slurp_file(const std::string& input_file, size_t buf_size) const {
6,312✔
162
   std::vector<uint8_t> buf;
6,312✔
163
   auto insert_fn = [&](const uint8_t b[], size_t l) { buf.insert(buf.end(), b, b + l); };
6,316✔
164
   Command::read_file(input_file, insert_fn, buf_size);
6,312✔
165
   return buf;
6,312✔
166
}
×
167

168
std::string Command::slurp_file_as_str(const std::string& input_file, size_t buf_size) const {
17✔
169
   std::string str;
17✔
170
   auto insert_fn = [&](const uint8_t b[], size_t l) { str.append(reinterpret_cast<const char*>(b), l); };
19✔
171
   Command::read_file(input_file, insert_fn, buf_size);
19✔
172
   return str;
15✔
173
}
2✔
174

175
void Command::read_file(const std::string& input_file,
6,370✔
176
                        const std::function<void(uint8_t[], size_t)>& consumer_fn,
177
                        size_t buf_size) {
178
   if(input_file == "-") {
6,370✔
179
      do_read_file(std::cin, consumer_fn, buf_size);
6,293✔
180
   } else {
181
      std::ifstream in(input_file, std::ios::binary);
77✔
182
      if(!in) {
77✔
183
         throw CLI_IO_Error("reading file", input_file);
4✔
184
      }
185
      do_read_file(in, consumer_fn, buf_size);
75✔
186
   }
77✔
187
}
6,368✔
188

189
//static
190
void Command::do_read_file(std::istream& in,
6,368✔
191
                           const std::function<void(uint8_t[], size_t)>& consumer_fn,
192
                           size_t buf_size) {
193
   // Avoid an infinite loop on --buf-size=0
194
   std::vector<uint8_t> buf(buf_size == 0 ? 4096 : buf_size);
6,430✔
195

196
   while(in.good()) {
12,759✔
197
      in.read(reinterpret_cast<char*>(buf.data()), buf.size());
6,391✔
198
      const size_t got = static_cast<size_t>(in.gcount());
6,391✔
199
      consumer_fn(buf.data(), got);
12,782✔
200
   }
201
}
6,368✔
202

203
Botan::RandomNumberGenerator& Command::rng() {
5,479✔
204
   return *rng_as_shared();
5,479✔
205
}
206

207
std::shared_ptr<Botan::RandomNumberGenerator> Command::rng_as_shared() {
5,494✔
208
   if(m_rng == nullptr) {
5,494✔
209
      m_rng = cli_make_rng(get_arg("rng-type"), get_arg("drbg-seed"));
461✔
210
   }
211

212
   return m_rng;
5,494✔
213
}
214

215
std::string Command::get_passphrase_arg(const std::string& prompt, const std::string& opt_name) {
89✔
216
   std::string s = get_arg(opt_name);
89✔
217
   if(s != "-") {
89✔
218
      return s;
178✔
219
   }
220
   return get_passphrase(prompt);
×
221
}
89✔
222

223
namespace {
224

225
bool echo_suppression_supported() {
×
226
   auto echo = Botan::OS::suppress_echo_on_terminal();
×
227
   return (echo != nullptr);
×
228
}
×
229

230
}  // namespace
231

232
std::string Command::get_passphrase(const std::string& prompt) {
×
233
   if(echo_suppression_supported() == false) {
×
234
      error_output() << "Warning: terminal echo suppression not enabled for this platform\n";
×
235
   }
236

237
   error_output() << prompt << ": " << std::flush;
×
238
   std::string pass;
×
239

240
   auto echo_suppress = Botan::OS::suppress_echo_on_terminal();
×
241

242
   std::getline(std::cin, pass);
×
243

244
   return pass;
×
245
}
×
246

247
//static
248
std::string Command::format_blob(const std::string& format, const uint8_t bits[], size_t len) {
41✔
249
#if defined(BOTAN_HAS_HEX_CODEC)
250
   if(format == "hex") {
41✔
251
      return Botan::hex_encode(bits, len);
35✔
252
   }
253
#endif
254

255
#if defined(BOTAN_HAS_BASE64_CODEC)
256
   if(format == "base64") {
6✔
257
      return Botan::base64_encode(bits, len);
2✔
258
   }
259
#endif
260

261
#if defined(BOTAN_HAS_BASE58_CODEC)
262
   if(format == "base58") {
4✔
263
      return Botan::base58_encode(bits, len);
2✔
264
   }
265
   if(format == "base58check") {
2✔
266
      return Botan::base58_check_encode(bits, len);
2✔
267
   }
268
#endif
269

270
   // If we supported format, we would have already returned
271
   throw CLI_Usage_Error("Unknown or unsupported format type");
×
272
}
273

274
// Registration code
275

276
Command::Registration::Registration(const std::string& name, const Command::cmd_maker_fn& maker_fn) {
473,472✔
277
   std::map<std::string, Command::cmd_maker_fn>& reg = Command::global_registry();
473,472✔
278

279
   if(reg.contains(name)) {
473,472✔
280
      throw CLI_Error("Duplicated registration of command " + name);
×
281
   }
282

283
   reg.insert(std::make_pair(name, maker_fn));
473,472✔
284
}
473,472✔
285

286
//static
287
std::map<std::string, Command::cmd_maker_fn>& Command::global_registry() {
480,130✔
288
   static std::map<std::string, Command::cmd_maker_fn> g_cmds;
480,130✔
289
   return g_cmds;
480,130✔
290
}
291

292
//static
293
std::vector<std::string> Command::registered_cmds() {
10✔
294
   std::vector<std::string> cmds;
10✔
295
   for(auto& cmd : Command::global_registry()) {
730✔
296
      cmds.push_back(cmd.first);
720✔
297
   }
298
   return cmds;
10✔
299
}
×
300

301
//static
302
std::unique_ptr<Command> Command::get_cmd(const std::string& name) {
6,648✔
303
   const auto& reg = Command::global_registry();
6,648✔
304

305
   auto i = reg.find(name);
6,648✔
306
   if(i != reg.end()) {
6,648✔
307
      return i->second();
6,648✔
308
   }
309

310
   return nullptr;
×
311
}
312

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