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

randombit / botan / 5079590438

25 May 2023 12:28PM UTC coverage: 92.228% (+0.5%) from 91.723%
5079590438

Pull #3502

github

Pull Request #3502: Apply clang-format to the codebase

75589 of 81959 relevant lines covered (92.23%)

12139530.51 hits per line

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

82.41
/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,707✔
53
   std::vector<std::string> elems;
6,707✔
54
   if(str.empty())
6,707✔
55
      return elems;
56

57
   std::string substr;
6,610✔
58
   for(auto i = str.begin(); i != str.end(); ++i) {
567,697✔
59
      if(*i == delim) {
561,087✔
60
         if(!substr.empty())
45,533✔
61
            elems.push_back(substr);
45,532✔
62
         substr.clear();
45,533✔
63
      } else
64
         substr += *i;
1,076,641✔
65
   }
66

67
   if(substr.empty())
6,610✔
68
      throw CLI_Error("Unable to split string: " + str);
×
69
   elems.push_back(substr);
6,610✔
70

71
   return elems;
6,610✔
72
}
6,707✔
73

74
bool Argument_Parser::flag_set(const std::string& flag_name) const { return m_user_flags.count(flag_name) > 0; }
19,929✔
75

76
bool Argument_Parser::has_arg(const std::string& opt_name) const { return m_user_args.count(opt_name) > 0; }
26,276✔
77

78
std::string Argument_Parser::get_arg(const std::string& opt_name) const {
33,368✔
79
   auto i = m_user_args.find(opt_name);
33,368✔
80
   if(i == m_user_args.end()) {
33,368✔
81
      // this shouldn't occur unless you passed the wrong thing to get_arg
82
      throw CLI_Error("Unknown option " + opt_name + " used (program bug)");
×
83
   }
84
   return i->second;
33,368✔
85
}
86

87
std::string Argument_Parser::get_arg_or(const std::string& opt_name, const std::string& otherwise) const {
18,845✔
88
   auto i = m_user_args.find(opt_name);
18,845✔
89
   if(i == m_user_args.end() || i->second.empty()) {
18,845✔
90
      return otherwise;
2,837✔
91
   }
92
   return i->second;
16,008✔
93
}
94

95
size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const {
6,516✔
96
   const std::string s = get_arg(opt_name);
6,516✔
97

98
   try {
6,516✔
99
      return static_cast<size_t>(std::stoul(s));
6,516✔
100
   } catch(std::exception&) { throw CLI_Usage_Error("Invalid integer value '" + s + "' for option " + opt_name); }
×
101
}
6,516✔
102

103
std::vector<std::string> Argument_Parser::get_arg_list(const std::string& what) const {
102✔
104
   if(what == m_spec_rest)
102✔
105
      return m_user_rest;
100✔
106

107
   return split_on(get_arg(what), ',');
4✔
108
}
109

110
void Argument_Parser::parse_args(const std::vector<std::string>& params) {
6,570✔
111
   std::vector<std::string> args;
6,570✔
112
   for(const auto& param : params) {
39,714✔
113
      if(param.find("--") == 0) {
33,144✔
114
         // option
115
         const auto eq = param.find('=');
26,488✔
116

117
         if(eq == std::string::npos) {
26,488✔
118
            const std::string opt_name = param.substr(2, std::string::npos);
3,199✔
119

120
            if(m_spec_flags.count(opt_name) == 0) {
3,199✔
121
               if(m_spec_opts.count(opt_name)) {
×
122
                  throw CLI_Usage_Error("Invalid usage of option --" + opt_name + " without value");
×
123
               } else {
124
                  throw CLI_Usage_Error("Unknown flag --" + opt_name);
×
125
               }
126
            }
127
            m_user_flags.insert(opt_name);
3,199✔
128
         } else {
3,199✔
129
            const std::string opt_name = param.substr(2, eq - 2);
23,289✔
130
            const std::string opt_val = param.substr(eq + 1, std::string::npos);
23,289✔
131

132
            if(m_spec_opts.count(opt_name) == 0) {
23,289✔
133
               throw CLI_Usage_Error("Unknown option --" + opt_name);
×
134
            }
135

136
            if(m_user_args.contains(opt_name)) {
23,289✔
137
               throw CLI_Usage_Error("Duplicated option --" + opt_name);
×
138
            }
139

140
            m_user_args.insert(std::make_pair(opt_name, opt_val));
23,289✔
141
         }
23,305✔
142
      } else {
143
         // argument
144
         args.push_back(param);
6,656✔
145
      }
146
   }
147

148
   if(flag_set("help"))
13,140✔
149
      return;
×
150

151
   if(args.size() < m_spec_args.size()) {
6,570✔
152
      // not enough arguments
153
      throw CLI_Usage_Error("Invalid argument count, got " + std::to_string(args.size()) + " expected " +
×
154
                            std::to_string(m_spec_args.size()));
×
155
   }
156

157
   bool seen_stdin_flag = false;
158
   size_t arg_i = 0;
159
   for(const auto& arg : m_spec_args) {
13,091✔
160
      m_user_args.insert(std::make_pair(arg, args[arg_i]));
6,521✔
161

162
      if(args[arg_i] == "-") {
6,521✔
163
         if(seen_stdin_flag) {
6,286✔
164
            throw CLI_Usage_Error("Cannot specify '-' (stdin) more than once");
×
165
         }
166
         seen_stdin_flag = true;
167
      }
168

169
      ++arg_i;
6,521✔
170
   }
171

172
   if(m_spec_rest.empty()) {
6,570✔
173
      if(arg_i != args.size()) {
6,474✔
174
         throw CLI_Usage_Error("Too many arguments");
×
175
      }
176
   } else {
177
      m_user_rest.assign(args.begin() + arg_i, args.end());
96✔
178
   }
179

180
   // Now insert any defaults for options not supplied by the user
181
   for(const auto& opt : m_spec_opts) {
65,242✔
182
      if(m_user_args.count(opt.first) == 0) {
58,672✔
183
         m_user_args.insert(opt);
58,672✔
184
      }
185
   }
186
}
6,570✔
187

188
Argument_Parser::Argument_Parser(const std::string& spec,
6,570✔
189
                                 const std::vector<std::string>& extra_flags,
190
                                 const std::vector<std::string>& extra_opts) {
6,570✔
191
   class CLI_Error_Invalid_Spec final : public CLI_Error {
6,570✔
192
      public:
193
         explicit CLI_Error_Invalid_Spec(const std::string& bad_spec) :
×
194
               CLI_Error("Invalid command spec '" + bad_spec + "'") {}
×
195
   };
196

197
   const std::vector<std::string> parts = split_on(spec, ' ');
6,570✔
198

199
   if(parts.size() == 0) {
6,570✔
200
      throw CLI_Error_Invalid_Spec(spec);
×
201
   }
202

203
   for(size_t i = 1; i != parts.size(); ++i) {
52,100✔
204
      const std::string s = parts[i];
45,530✔
205

206
      if(s.empty())  // ?!? (shouldn't happen)
45,530✔
207
      {
208
         throw CLI_Error_Invalid_Spec(spec);
×
209
      }
210

211
      if(s.size() > 2 && s[0] == '-' && s[1] == '-') {
45,530✔
212
         // option or flag
213

214
         auto eq = s.find('=');
38,913✔
215

216
         if(eq == std::string::npos) {
38,913✔
217
            m_spec_flags.insert(s.substr(2, std::string::npos));
13,006✔
218
         } else {
219
            m_spec_opts.insert(std::make_pair(s.substr(2, eq - 2), s.substr(eq + 1, std::string::npos)));
64,820✔
220
         }
221
      } else if(s[0] == '*') {
6,617✔
222
         // rest argument
223
         if(m_spec_rest.empty() && s.size() > 2) {
96✔
224
            m_spec_rest = s.substr(1, std::string::npos);
96✔
225
         } else {
226
            throw CLI_Error_Invalid_Spec(spec);
×
227
         }
228
      } else {
229
         // named argument
230
         if(!m_spec_rest.empty())  // rest arg wasn't last
6,521✔
231
         {
232
            throw CLI_Error_Invalid_Spec(spec);
×
233
         }
234

235
         m_spec_args.push_back(s);
6,521✔
236
      }
237
   }
45,530✔
238

239
   for(std::string flag : extra_flags)
19,708✔
240
      m_spec_flags.insert(flag);
13,138✔
241
   for(std::string opt : extra_opts)
32,846✔
242
      m_spec_opts.insert(std::make_pair(opt, ""));
26,276✔
243
}
6,570✔
244

245
}
246

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