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

randombit / botan / 11214575493

07 Oct 2024 11:30AM UTC coverage: 91.002% (-0.3%) from 91.28%
11214575493

push

github

web-flow
Merge pull request #4337 from Rohde-Schwarz/feature/tpm2_support

Feature: TPM2 Support

89410 of 98251 relevant lines covered (91.0%)

8897349.05 hits per line

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

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

7
#ifndef BOTAN_CLI_ARGPARSE_H_
8
#define BOTAN_CLI_ARGPARSE_H_
9

10
#include "cli_exceptions.h"
11
#include <map>
12
#include <set>
13
#include <string>
14
#include <vector>
15

16
namespace Botan_CLI {
17

18
class Argument_Parser final {
19
   public:
20
      Argument_Parser(const std::string& spec,
21
                      const std::vector<std::string>& extra_flags = {},
22
                      const std::vector<std::string>& extra_opts = {});
23

24
      void parse_args(const std::vector<std::string>& params);
25

26
      bool flag_set(const std::string& flag) const;
27

28
      bool has_arg(const std::string& opt_name) const;
29
      std::string get_arg(const std::string& option) const;
30

31
      std::string get_arg_or(const std::string& option, const std::string& otherwise) const;
32

33
      size_t get_arg_sz(const std::string& option) const;
34

35
      size_t get_arg_hex_sz_or(const std::string& opt_name, const std::string& otherwise) const;
36

37
      std::vector<std::string> get_arg_list(const std::string& what) const;
38

39
      static std::vector<std::string> split_on(const std::string& str, char delim);
40

41
   private:
42
      // set in constructor
43
      std::vector<std::string> m_spec_args;
44
      std::set<std::string> m_spec_flags;
45
      std::map<std::string, std::string> m_spec_opts;
46
      std::string m_spec_rest;
47

48
      // set in parse_args()
49
      std::map<std::string, std::string> m_user_args;
50
      std::set<std::string> m_user_flags;
51
      std::vector<std::string> m_user_rest;
52
};
53

54
std::vector<std::string> Argument_Parser::split_on(const std::string& str, char delim) {
6,736✔
55
   std::vector<std::string> elems;
6,736✔
56
   if(str.empty()) {
6,736✔
57
      return elems;
58
   }
59

60
   std::string substr;
6,632✔
61
   for(auto i = str.begin(); i != str.end(); ++i) {
571,098✔
62
      if(*i == delim) {
564,466✔
63
         if(!substr.empty()) {
45,749✔
64
            elems.push_back(substr);
45,749✔
65
         }
66
         substr.clear();
45,749✔
67
      } else {
68
         substr += *i;
1,083,183✔
69
      }
70
   }
71

72
   if(substr.empty()) {
6,632✔
73
      throw CLI_Error("Unable to split string: " + str);
×
74
   }
75
   elems.push_back(substr);
6,632✔
76

77
   return elems;
6,632✔
78
}
6,632✔
79

80
bool Argument_Parser::flag_set(const std::string& flag_name) const {
20,087✔
81
   return m_user_flags.contains(flag_name);
13,187✔
82
}
83

84
bool Argument_Parser::has_arg(const std::string& opt_name) const {
13,184✔
85
   return m_user_args.contains(opt_name);
13,184✔
86
}
87

88
std::string Argument_Parser::get_arg(const std::string& opt_name) const {
33,570✔
89
   auto i = m_user_args.find(opt_name);
33,570✔
90
   if(i == m_user_args.end()) {
33,570✔
91
      // this shouldn't occur unless you passed the wrong thing to get_arg
92
      throw CLI_Error("Unknown option " + opt_name + " used (program bug)");
×
93
   }
94
   return i->second;
33,570✔
95
}
96

97
std::string Argument_Parser::get_arg_or(const std::string& opt_name, const std::string& otherwise) const {
18,848✔
98
   auto i = m_user_args.find(opt_name);
18,848✔
99
   if(i == m_user_args.end() || i->second.empty()) {
18,848✔
100
      return otherwise;
2,837✔
101
   }
102
   return i->second;
16,011✔
103
}
104

105
size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const {
6,531✔
106
   const std::string s = get_arg(opt_name);
6,531✔
107

108
   try {
6,531✔
109
      return static_cast<size_t>(std::stoul(s));
6,531✔
110
   } catch(std::exception&) {
×
111
      throw CLI_Usage_Error("Invalid integer value '" + s + "' for option " + opt_name);
×
112
   }
×
113
}
6,531✔
114

115
size_t Argument_Parser::get_arg_hex_sz_or(const std::string& opt_name, const std::string& otherwise) const {
2✔
116
   const std::string s = get_arg_or(opt_name, otherwise);
2✔
117

118
   try {
2✔
119
      return static_cast<size_t>(std::stoul(s, nullptr, 16));
2✔
120
   } catch(std::exception&) {
×
121
      throw CLI_Usage_Error("Invalid hex integer value '" + s + "' for option " + opt_name);
×
122
   }
×
123
}
2✔
124

125
std::vector<std::string> Argument_Parser::get_arg_list(const std::string& what) const {
109✔
126
   if(what == m_spec_rest) {
109✔
127
      return m_user_rest;
107✔
128
   }
129

130
   return split_on(get_arg(what), ',');
4✔
131
}
132

133
void Argument_Parser::parse_args(const std::vector<std::string>& params) {
6,593✔
134
   std::vector<std::string> args;
6,593✔
135
   for(const auto& param : params) {
39,840✔
136
      if(param.find("--") == 0) {
33,247✔
137
         // option
138
         const auto eq = param.find('=');
26,563✔
139

140
         if(eq == std::string::npos) {
26,563✔
141
            const std::string opt_name = param.substr(2, std::string::npos);
3,207✔
142

143
            if(!m_spec_flags.contains(opt_name)) {
3,207✔
144
               if(m_spec_opts.contains(opt_name)) {
×
145
                  throw CLI_Usage_Error("Invalid usage of option --" + opt_name + " without value");
×
146
               } else {
147
                  throw CLI_Usage_Error("Unknown flag --" + opt_name);
×
148
               }
149
            }
150
            m_user_flags.insert(opt_name);
3,207✔
151
         } else {
3,207✔
152
            const std::string opt_name = param.substr(2, eq - 2);
23,356✔
153
            const std::string opt_val = param.substr(eq + 1, std::string::npos);
23,356✔
154

155
            if(!m_spec_opts.contains(opt_name)) {
23,356✔
156
               throw CLI_Usage_Error("Unknown option --" + opt_name);
×
157
            }
158

159
            if(m_user_args.contains(opt_name)) {
23,356✔
160
               throw CLI_Usage_Error("Duplicated option --" + opt_name);
×
161
            }
162

163
            m_user_args.insert(std::make_pair(opt_name, opt_val));
46,712✔
164
         }
23,356✔
165
      } else {
166
         // argument
167
         args.push_back(param);
6,684✔
168
      }
169
   }
170

171
   if(flag_set("help")) {
6,593✔
172
      return;
×
173
   }
174

175
   if(args.size() < m_spec_args.size()) {
6,593✔
176
      // not enough arguments
177
      throw CLI_Usage_Error("Invalid argument count, got " + std::to_string(args.size()) + " expected " +
×
178
                            std::to_string(m_spec_args.size()));
×
179
   }
180

181
   bool seen_stdin_flag = false;
182
   size_t arg_i = 0;
183
   for(const auto& arg : m_spec_args) {
13,134✔
184
      m_user_args.insert(std::make_pair(arg, args[arg_i]));
6,541✔
185

186
      if(args[arg_i] == "-") {
6,541✔
187
         if(seen_stdin_flag) {
6,287✔
188
            throw CLI_Usage_Error("Cannot specify '-' (stdin) more than once");
×
189
         }
190
         seen_stdin_flag = true;
191
      }
192

193
      ++arg_i;
6,541✔
194
   }
195

196
   if(m_spec_rest.empty()) {
6,593✔
197
      if(arg_i != args.size()) {
6,490✔
198
         throw CLI_Usage_Error("Too many arguments");
×
199
      }
200
   } else {
201
      m_user_rest.assign(args.begin() + arg_i, args.end());
103✔
202
   }
203

204
   // Now insert any defaults for options not supplied by the user
205
   for(const auto& opt : m_spec_opts) {
65,492✔
206
      if(!m_user_args.contains(opt.first)) {
58,899✔
207
         m_user_args.insert(opt);
58,899✔
208
      }
209
   }
210
}
6,593✔
211

212
Argument_Parser::Argument_Parser(const std::string& spec,
6,593✔
213
                                 const std::vector<std::string>& extra_flags,
214
                                 const std::vector<std::string>& extra_opts) {
6,593✔
215
   class CLI_Error_Invalid_Spec final : public CLI_Error {
6,593✔
216
      public:
217
         explicit CLI_Error_Invalid_Spec(const std::string& bad_spec) :
×
218
               CLI_Error("Invalid command spec '" + bad_spec + "'") {}
×
219
   };
220

221
   const std::vector<std::string> parts = split_on(spec, ' ');
6,593✔
222

223
   if(parts.empty()) {
6,593✔
224
      throw CLI_Error_Invalid_Spec(spec);
×
225
   }
226

227
   for(size_t i = 1; i != parts.size(); ++i) {
52,340✔
228
      const auto& s = parts[i];
45,747✔
229

230
      if(s.empty()) {
45,747✔
231
         // ?!? (shouldn't happen)
232
         throw CLI_Error_Invalid_Spec(spec);
×
233
      }
234

235
      if(s.size() > 2 && s[0] == '-' && s[1] == '-') {
45,747✔
236
         // option or flag
237

238
         auto eq = s.find('=');
39,103✔
239

240
         if(eq == std::string::npos) {
39,103✔
241
            m_spec_flags.insert(s.substr(2, std::string::npos));
13,114✔
242
         } else {
243
            m_spec_opts.insert(std::make_pair(s.substr(2, eq - 2), s.substr(eq + 1, std::string::npos)));
97,638✔
244
         }
245
      } else if(s[0] == '*') {
6,644✔
246
         // rest argument
247
         if(m_spec_rest.empty() && s.size() > 2) {
103✔
248
            m_spec_rest = s.substr(1, std::string::npos);
103✔
249
         } else {
250
            throw CLI_Error_Invalid_Spec(spec);
×
251
         }
252
      } else {
253
         // named argument
254
         if(!m_spec_rest.empty())  // rest arg wasn't last
6,541✔
255
         {
256
            throw CLI_Error_Invalid_Spec(spec);
×
257
         }
258

259
         m_spec_args.push_back(s);
6,541✔
260
      }
261
   }
262

263
   for(const std::string& flag : extra_flags) {
19,777✔
264
      m_spec_flags.insert(flag);
13,184✔
265
   }
266
   for(const std::string& opt : extra_opts) {
32,961✔
267
      m_spec_opts.insert(std::make_pair(opt, ""));
52,736✔
268
   }
269
}
6,593✔
270

271
}  // namespace Botan_CLI
272

273
#endif
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