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

randombit / botan / 5230455705

10 Jun 2023 02:30PM UTC coverage: 91.715% (-0.03%) from 91.746%
5230455705

push

github

randombit
Merge GH #3584 Change clang-format AllowShortFunctionsOnASingleLine config from All to Inline

77182 of 84154 relevant lines covered (91.72%)

11975295.43 hits per line

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

81.25
/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
   std::string substr;
6,617✔
58
   for(auto i = str.begin(); i != str.end(); ++i) {
568,263✔
59
      if(*i == delim) {
561,646✔
60
         if(!substr.empty())
45,582✔
61
            elems.push_back(substr);
45,581✔
62
         substr.clear();
45,582✔
63
      } else
64
         substr += *i;
1,077,710✔
65
   }
66

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

71
   return elems;
6,617✔
72
}
6,717✔
73

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

78
bool Argument_Parser::has_arg(const std::string& opt_name) const {
13,152✔
79
   return m_user_args.count(opt_name) > 0;
26,304✔
80
}
81

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

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

99
size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const {
6,523✔
100
   const std::string s = get_arg(opt_name);
6,523✔
101

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

109
std::vector<std::string> Argument_Parser::get_arg_list(const std::string& what) const {
106✔
110
   if(what == m_spec_rest)
106✔
111
      return m_user_rest;
104✔
112

113
   return split_on(get_arg(what), ',');
4✔
114
}
115

116
void Argument_Parser::parse_args(const std::vector<std::string>& params) {
6,577✔
117
   std::vector<std::string> args;
6,577✔
118
   for(const auto& param : params) {
39,756✔
119
      if(param.find("--") == 0) {
33,179✔
120
         // option
121
         const auto eq = param.find('=');
26,511✔
122

123
         if(eq == std::string::npos) {
26,511✔
124
            const std::string opt_name = param.substr(2, std::string::npos);
3,204✔
125

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

138
            if(m_spec_opts.count(opt_name) == 0) {
23,307✔
139
               throw CLI_Usage_Error("Unknown option --" + opt_name);
×
140
            }
141

142
            if(m_user_args.contains(opt_name)) {
23,307✔
143
               throw CLI_Usage_Error("Duplicated option --" + opt_name);
×
144
            }
145

146
            m_user_args.insert(std::make_pair(opt_name, opt_val));
23,307✔
147
         }
23,323✔
148
      } else {
149
         // argument
150
         args.push_back(param);
6,668✔
151
      }
152
   }
153

154
   if(flag_set("help"))
13,154✔
155
      return;
×
156

157
   if(args.size() < m_spec_args.size()) {
6,577✔
158
      // not enough arguments
159
      throw CLI_Usage_Error("Invalid argument count, got " + std::to_string(args.size()) + " expected " +
×
160
                            std::to_string(m_spec_args.size()));
×
161
   }
162

163
   bool seen_stdin_flag = false;
164
   size_t arg_i = 0;
165
   for(const auto& arg : m_spec_args) {
13,106✔
166
      m_user_args.insert(std::make_pair(arg, args[arg_i]));
6,529✔
167

168
      if(args[arg_i] == "-") {
6,529✔
169
         if(seen_stdin_flag) {
6,286✔
170
            throw CLI_Usage_Error("Cannot specify '-' (stdin) more than once");
×
171
         }
172
         seen_stdin_flag = true;
173
      }
174

175
      ++arg_i;
6,529✔
176
   }
177

178
   if(m_spec_rest.empty()) {
6,577✔
179
      if(arg_i != args.size()) {
6,477✔
180
         throw CLI_Usage_Error("Too many arguments");
×
181
      }
182
   } else {
183
      m_user_rest.assign(args.begin() + arg_i, args.end());
100✔
184
   }
185

186
   // Now insert any defaults for options not supplied by the user
187
   for(const auto& opt : m_spec_opts) {
65,308✔
188
      if(m_user_args.count(opt.first) == 0) {
58,731✔
189
         m_user_args.insert(opt);
58,731✔
190
      }
191
   }
192
}
6,577✔
193

194
Argument_Parser::Argument_Parser(const std::string& spec,
6,577✔
195
                                 const std::vector<std::string>& extra_flags,
196
                                 const std::vector<std::string>& extra_opts) {
6,577✔
197
   class CLI_Error_Invalid_Spec final : public CLI_Error {
6,577✔
198
      public:
199
         explicit CLI_Error_Invalid_Spec(const std::string& bad_spec) :
×
200
               CLI_Error("Invalid command spec '" + bad_spec + "'") {}
×
201
   };
202

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

205
   if(parts.size() == 0) {
6,577✔
206
      throw CLI_Error_Invalid_Spec(spec);
×
207
   }
208

209
   for(size_t i = 1; i != parts.size(); ++i) {
52,156✔
210
      const std::string s = parts[i];
45,579✔
211

212
      if(s.empty())  // ?!? (shouldn't happen)
45,579✔
213
      {
214
         throw CLI_Error_Invalid_Spec(spec);
×
215
      }
216

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

220
         auto eq = s.find('=');
38,950✔
221

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

241
         m_spec_args.push_back(s);
6,529✔
242
      }
243
   }
45,579✔
244

245
   for(std::string flag : extra_flags)
19,729✔
246
      m_spec_flags.insert(flag);
13,152✔
247
   for(std::string opt : extra_opts)
32,881✔
248
      m_spec_opts.insert(std::make_pair(opt, ""));
26,304✔
249
}
6,577✔
250

251
}  // namespace Botan_CLI
252

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