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

randombit / botan / 5696981105

28 Jul 2023 12:55PM UTC coverage: 91.69% (+0.005%) from 91.685%
5696981105

push

github

randombit
Merge GH #3649 Remove some macros from loadstor.h

78267 of 85360 relevant lines covered (91.69%)

12322548.38 hits per line

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

81.08
/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
      std::vector<std::string> get_arg_list(const std::string& what) const;
36

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

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

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

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

58
   std::string substr;
6,617✔
59
   for(auto i = str.begin(); i != str.end(); ++i) {
568,263✔
60
      if(*i == delim) {
561,646✔
61
         if(!substr.empty()) {
45,582✔
62
            elems.push_back(substr);
45,581✔
63
         }
64
         substr.clear();
45,582✔
65
      } else {
66
         substr += *i;
1,077,710✔
67
      }
68
   }
69

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

75
   return elems;
6,617✔
76
}
6,717✔
77

78
bool Argument_Parser::flag_set(const std::string& flag_name) const {
19,977✔
79
   return m_user_flags.contains(flag_name);
13,155✔
80
}
81

82
bool Argument_Parser::has_arg(const std::string& opt_name) const {
13,152✔
83
   return m_user_args.contains(opt_name);
13,152✔
84
}
85

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

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

103
size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const {
6,523✔
104
   const std::string s = get_arg(opt_name);
6,523✔
105

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

113
std::vector<std::string> Argument_Parser::get_arg_list(const std::string& what) const {
106✔
114
   if(what == m_spec_rest) {
106✔
115
      return m_user_rest;
104✔
116
   }
117

118
   return split_on(get_arg(what), ',');
4✔
119
}
120

121
void Argument_Parser::parse_args(const std::vector<std::string>& params) {
6,577✔
122
   std::vector<std::string> args;
6,577✔
123
   for(const auto& param : params) {
39,757✔
124
      if(param.find("--") == 0) {
33,180✔
125
         // option
126
         const auto eq = param.find('=');
26,511✔
127

128
         if(eq == std::string::npos) {
26,511✔
129
            const std::string opt_name = param.substr(2, std::string::npos);
3,204✔
130

131
            if(!m_spec_flags.contains(opt_name)) {
3,204✔
132
               if(m_spec_opts.contains(opt_name)) {
×
133
                  throw CLI_Usage_Error("Invalid usage of option --" + opt_name + " without value");
×
134
               } else {
135
                  throw CLI_Usage_Error("Unknown flag --" + opt_name);
×
136
               }
137
            }
138
            m_user_flags.insert(opt_name);
3,204✔
139
         } else {
3,204✔
140
            const std::string opt_name = param.substr(2, eq - 2);
23,307✔
141
            const std::string opt_val = param.substr(eq + 1, std::string::npos);
23,307✔
142

143
            if(!m_spec_opts.contains(opt_name)) {
23,307✔
144
               throw CLI_Usage_Error("Unknown option --" + opt_name);
×
145
            }
146

147
            if(m_user_args.contains(opt_name)) {
23,307✔
148
               throw CLI_Usage_Error("Duplicated option --" + opt_name);
×
149
            }
150

151
            m_user_args.insert(std::make_pair(opt_name, opt_val));
23,307✔
152
         }
23,323✔
153
      } else {
154
         // argument
155
         args.push_back(param);
6,669✔
156
      }
157
   }
158

159
   if(flag_set("help")) {
6,577✔
160
      return;
×
161
   }
162

163
   if(args.size() < m_spec_args.size()) {
6,577✔
164
      // not enough arguments
165
      throw CLI_Usage_Error("Invalid argument count, got " + std::to_string(args.size()) + " expected " +
×
166
                            std::to_string(m_spec_args.size()));
×
167
   }
168

169
   bool seen_stdin_flag = false;
170
   size_t arg_i = 0;
171
   for(const auto& arg : m_spec_args) {
13,106✔
172
      m_user_args.insert(std::make_pair(arg, args[arg_i]));
6,529✔
173

174
      if(args[arg_i] == "-") {
6,529✔
175
         if(seen_stdin_flag) {
6,286✔
176
            throw CLI_Usage_Error("Cannot specify '-' (stdin) more than once");
×
177
         }
178
         seen_stdin_flag = true;
179
      }
180

181
      ++arg_i;
6,529✔
182
   }
183

184
   if(m_spec_rest.empty()) {
6,577✔
185
      if(arg_i != args.size()) {
6,477✔
186
         throw CLI_Usage_Error("Too many arguments");
×
187
      }
188
   } else {
189
      m_user_rest.assign(args.begin() + arg_i, args.end());
100✔
190
   }
191

192
   // Now insert any defaults for options not supplied by the user
193
   for(const auto& opt : m_spec_opts) {
65,308✔
194
      if(!m_user_args.contains(opt.first)) {
58,731✔
195
         m_user_args.insert(opt);
58,731✔
196
      }
197
   }
198
}
6,577✔
199

200
Argument_Parser::Argument_Parser(const std::string& spec,
6,577✔
201
                                 const std::vector<std::string>& extra_flags,
202
                                 const std::vector<std::string>& extra_opts) {
6,577✔
203
   class CLI_Error_Invalid_Spec final : public CLI_Error {
6,577✔
204
      public:
205
         explicit CLI_Error_Invalid_Spec(const std::string& bad_spec) :
×
206
               CLI_Error("Invalid command spec '" + bad_spec + "'") {}
×
207
   };
208

209
   const std::vector<std::string> parts = split_on(spec, ' ');
6,577✔
210

211
   if(parts.empty()) {
6,577✔
212
      throw CLI_Error_Invalid_Spec(spec);
×
213
   }
214

215
   for(size_t i = 1; i != parts.size(); ++i) {
52,156✔
216
      const auto& s = parts[i];
45,579✔
217

218
      if(s.empty()) {
45,579✔
219
         // ?!? (shouldn't happen)
220
         throw CLI_Error_Invalid_Spec(spec);
×
221
      }
222

223
      if(s.size() > 2 && s[0] == '-' && s[1] == '-') {
45,579✔
224
         // option or flag
225

226
         auto eq = s.find('=');
38,950✔
227

228
         if(eq == std::string::npos) {
38,950✔
229
            m_spec_flags.insert(s.substr(2, std::string::npos));
13,018✔
230
         } else {
231
            m_spec_opts.insert(std::make_pair(s.substr(2, eq - 2), s.substr(eq + 1, std::string::npos)));
64,882✔
232
         }
233
      } else if(s[0] == '*') {
6,629✔
234
         // rest argument
235
         if(m_spec_rest.empty() && s.size() > 2) {
100✔
236
            m_spec_rest = s.substr(1, std::string::npos);
100✔
237
         } else {
238
            throw CLI_Error_Invalid_Spec(spec);
×
239
         }
240
      } else {
241
         // named argument
242
         if(!m_spec_rest.empty())  // rest arg wasn't last
6,529✔
243
         {
244
            throw CLI_Error_Invalid_Spec(spec);
×
245
         }
246

247
         m_spec_args.push_back(s);
6,529✔
248
      }
249
   }
250

251
   for(const std::string& flag : extra_flags) {
19,729✔
252
      m_spec_flags.insert(flag);
13,152✔
253
   }
254
   for(const std::string& opt : extra_opts) {
32,881✔
255
      m_spec_opts.insert(std::make_pair(opt, ""));
26,304✔
256
   }
257
}
6,577✔
258

259
}  // namespace Botan_CLI
260

261
#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