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

randombit / botan / 5123321399

30 May 2023 04:06PM UTC coverage: 92.213% (+0.004%) from 92.209%
5123321399

Pull #3558

github

web-flow
Merge dd72f7389 into 057bcbc35
Pull Request #3558: Add braces around all if/else statements

75602 of 81986 relevant lines covered (92.21%)

11859779.3 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
         }
62
      }
6,569✔
63

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

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

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

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

95
bool Command::flag_set(const std::string& flag_name) const { return m_args->flag_set(flag_name); }
13,351✔
96

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

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

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

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

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

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

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

135
std::ostream& Command::output() {
8,361✔
136
   if(m_output_stream) {
8,361✔
137
      return *m_output_stream;
319✔
138
   }
139
   return std::cout;
140
}
141

142
std::ostream& Command::error_output() {
13✔
143
   if(m_error_output_stream) {
13✔
144
      return *m_error_output_stream;
×
145
   }
146
   return std::cerr;
147
}
148

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

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

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

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

184
   while(in.good()) {
12,753✔
185
      in.read(reinterpret_cast<char*>(buf.data()), buf.size());
6,388✔
186
      const size_t got = static_cast<size_t>(in.gcount());
6,388✔
187
      consumer_fn(buf.data(), got);
12,776✔
188
   }
189
}
6,365✔
190

191
Botan::RandomNumberGenerator& Command::rng() { return *rng_as_shared(); }
4,896✔
192

193
std::shared_ptr<Botan::RandomNumberGenerator> Command::rng_as_shared() {
4,911✔
194
   if(m_rng == nullptr) {
4,911✔
195
      m_rng = cli_make_rng(get_arg("rng-type"), get_arg("drbg-seed"));
449✔
196
   }
197

198
   return m_rng;
4,911✔
199
}
200

201
std::string Command::get_passphrase_arg(const std::string& prompt, const std::string& opt_name) {
74✔
202
   std::string s = get_arg(opt_name);
74✔
203
   if(s != "-") {
74✔
204
      return s;
148✔
205
   }
206
   return get_passphrase(prompt);
×
207
}
74✔
208

209
namespace {
210

211
bool echo_suppression_supported() {
×
212
   auto echo = Botan::OS::suppress_echo_on_terminal();
×
213
   return (echo != nullptr);
×
214
}
×
215

216
}  // namespace
217

218
std::string Command::get_passphrase(const std::string& prompt) {
×
219
   if(echo_suppression_supported() == false) {
×
220
      error_output() << "Warning: terminal echo suppression not enabled for this platform\n";
×
221
   }
222

223
   error_output() << prompt << ": " << std::flush;
×
224
   std::string pass;
×
225

226
   auto echo_suppress = Botan::OS::suppress_echo_on_terminal();
×
227

228
   std::getline(std::cin, pass);
×
229

230
   return pass;
×
231
}
×
232

233
//static
234
std::string Command::format_blob(const std::string& format, const uint8_t bits[], size_t len) {
38✔
235
#if defined(BOTAN_HAS_HEX_CODEC)
236
   if(format == "hex") {
38✔
237
      return Botan::hex_encode(bits, len);
32✔
238
   }
239
#endif
240

241
#if defined(BOTAN_HAS_BASE64_CODEC)
242
   if(format == "base64") {
6✔
243
      return Botan::base64_encode(bits, len);
2✔
244
   }
245
#endif
246

247
#if defined(BOTAN_HAS_BASE58_CODEC)
248
   if(format == "base58") {
4✔
249
      return Botan::base58_encode(bits, len);
2✔
250
   }
251
   if(format == "base58check") {
2✔
252
      return Botan::base58_check_encode(bits, len);
2✔
253
   }
254
#endif
255

256
   // If we supported format, we would have already returned
257
   throw CLI_Usage_Error("Unknown or unsupported format type");
×
258
}
259

260
// Registration code
261

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

265
   if(reg.contains(name)) {
472,968✔
266
      throw CLI_Error("Duplicated registration of command " + name);
×
267
   }
268

269
   reg.insert(std::make_pair(name, maker_fn));
472,968✔
270
}
472,968✔
271

272
//static
273
std::map<std::string, Command::cmd_maker_fn>& Command::global_registry() {
479,619✔
274
   static std::map<std::string, Command::cmd_maker_fn> g_cmds;
479,619✔
275
   return g_cmds;
479,619✔
276
}
277

278
//static
279
std::vector<std::string> Command::registered_cmds() {
10✔
280
   std::vector<std::string> cmds;
10✔
281
   for(auto& cmd : Command::global_registry()) {
730✔
282
      cmds.push_back(cmd.first);
720✔
283
   }
284
   return cmds;
10✔
285
}
×
286

287
//static
288
std::unique_ptr<Command> Command::get_cmd(const std::string& name) {
6,641✔
289
   const auto& reg = Command::global_registry();
6,641✔
290

291
   auto i = reg.find(name);
6,641✔
292
   if(i != reg.end()) {
6,641✔
293
      return i->second();
6,641✔
294
   }
295

296
   return nullptr;
×
297
}
298

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