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

randombit / botan / 5134090420

31 May 2023 03:12PM UTC coverage: 91.721% (-0.3%) from 91.995%
5134090420

push

github

randombit
Merge GH #3565 Disable noisy/pointless pylint warnings

76048 of 82912 relevant lines covered (91.72%)

11755290.1 hits per line

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

80.91
/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,927✔
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&) {
×
101
      throw CLI_Usage_Error("Invalid integer value '" + s + "' for option " + opt_name);
×
102
   }
×
103
}
6,516✔
104

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

109
   return split_on(get_arg(what), ',');
4✔
110
}
111

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

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

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

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

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

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

150
   if(flag_set("help"))
13,140✔
151
      return;
×
152

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

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

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

171
      ++arg_i;
6,521✔
172
   }
173

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

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

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

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

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

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

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

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

216
         auto eq = s.find('=');
38,913✔
217

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

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

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

247
}  // namespace Botan_CLI
248

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