~ubuntu-branches/debian/sid/botan/sid

« back to all changes in this revision

Viewing changes to src/cli/argparse.h

  • Committer: Package Import Robot
  • Author(s): Laszlo Boszormenyi (GCS)
  • Date: 2018-03-01 22:23:25 UTC
  • mfrom: (1.2.2)
  • Revision ID: package-import@ubuntu.com-20180301222325-7p7vc45gu3hta34d
Tags: 2.4.0-2
* Don't remove .doctrees from the manual if it doesn't exist.
* Don't specify parallel to debhelper.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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_ARGPARSE_H_
 
8
#define BOTAN_ARGPARSE_H_
 
9
 
 
10
#include <string>
 
11
#include <map>
 
12
#include <set>
 
13
#include <vector>
 
14
#include <botan/parsing.h>
 
15
#include "cli_exceptions.h"
 
16
 
 
17
namespace Botan_CLI {
 
18
 
 
19
class Argument_Parser
 
20
   {
 
21
   public:
 
22
      Argument_Parser(const std::string& spec,
 
23
                      const std::vector<std::string>& extra_flags = {},
 
24
                      const std::vector<std::string>& extra_opts = {});
 
25
 
 
26
      void parse_args(const std::vector<std::string>& params);
 
27
 
 
28
      bool flag_set(const std::string& flag) const;
 
29
 
 
30
      bool has_arg(const std::string& opt_name) const;
 
31
      std::string get_arg(const std::string& option) const;
 
32
 
 
33
      std::string get_arg_or(const std::string& option, const std::string& otherwise) const;
 
34
 
 
35
      size_t get_arg_sz(const std::string& option) const;
 
36
 
 
37
      std::vector<std::string> get_arg_list(const std::string& what) const;
 
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
bool Argument_Parser::flag_set(const std::string& flag_name) const
 
53
   {
 
54
   return m_user_flags.count(flag_name) > 0;
 
55
   }
 
56
 
 
57
bool Argument_Parser::has_arg(const std::string& opt_name) const
 
58
   {
 
59
   return m_user_args.count(opt_name) > 0;
 
60
   }
 
61
 
 
62
std::string Argument_Parser::get_arg(const std::string& opt_name) const
 
63
   {
 
64
   auto i = m_user_args.find(opt_name);
 
65
   if(i == m_user_args.end())
 
66
      {
 
67
      // this shouldn't occur unless you passed the wrong thing to get_arg
 
68
      throw CLI_Error("Unknown option " + opt_name + " used (program bug)");
 
69
      }
 
70
   return i->second;
 
71
   }
 
72
 
 
73
std::string Argument_Parser::get_arg_or(const std::string& opt_name, const std::string& otherwise) const
 
74
   {
 
75
   auto i = m_user_args.find(opt_name);
 
76
   if(i == m_user_args.end() || i->second.empty())
 
77
      {
 
78
      return otherwise;
 
79
      }
 
80
   return i->second;
 
81
   }
 
82
 
 
83
size_t Argument_Parser::get_arg_sz(const std::string& opt_name) const
 
84
   {
 
85
   const std::string s = get_arg(opt_name);
 
86
 
 
87
   try
 
88
      {
 
89
      return static_cast<size_t>(std::stoul(s));
 
90
      }
 
91
   catch(std::exception&)
 
92
      {
 
93
      throw CLI_Usage_Error("Invalid integer value '" + s + "' for option " + opt_name);
 
94
      }
 
95
   }
 
96
 
 
97
std::vector<std::string> Argument_Parser::get_arg_list(const std::string& what) const
 
98
   {
 
99
   if(what != m_spec_rest)
 
100
      {
 
101
      throw CLI_Error("Unexpected list name '" + what + "'");
 
102
      }
 
103
   return m_user_rest;
 
104
   }
 
105
 
 
106
void Argument_Parser::parse_args(const std::vector<std::string>& params)
 
107
   {
 
108
   std::vector<std::string> args;
 
109
   for(auto const& param : params)
 
110
      {
 
111
      if(param.find("--") == 0)
 
112
         {
 
113
         // option
 
114
         const auto eq = param.find('=');
 
115
 
 
116
         if(eq == std::string::npos)
 
117
            {
 
118
            const std::string opt_name = param.substr(2, std::string::npos);
 
119
 
 
120
            if(m_spec_flags.count(opt_name) == 0)
 
121
               {
 
122
               if(m_spec_opts.count(opt_name))
 
123
                  {
 
124
                  throw CLI_Usage_Error("Invalid usage of option --" + opt_name +
 
125
                                        " without value");
 
126
                  }
 
127
               else
 
128
                  {
 
129
                  throw CLI_Usage_Error("Unknown flag --" + opt_name);
 
130
                  }
 
131
               }
 
132
            m_user_flags.insert(opt_name);
 
133
            }
 
134
         else
 
135
            {
 
136
            const std::string opt_name = param.substr(2, eq - 2);
 
137
            const std::string opt_val = param.substr(eq + 1, std::string::npos);
 
138
 
 
139
            if(m_spec_opts.count(opt_name) == 0)
 
140
               {
 
141
               throw CLI_Usage_Error("Unknown option --" + opt_name);
 
142
               }
 
143
 
 
144
            m_user_args.insert(std::make_pair(opt_name, opt_val));
 
145
            }
 
146
         }
 
147
      else
 
148
         {
 
149
         // argument
 
150
         args.push_back(param);
 
151
         }
 
152
      }
 
153
 
 
154
   bool seen_stdin_flag = false;
 
155
   size_t arg_i = 0;
 
156
   for(auto const& arg : m_spec_args)
 
157
      {
 
158
      if(arg_i >= args.size())
 
159
         {
 
160
         // not enough arguments
 
161
         throw CLI_Usage_Error("Invalid argument count, got " +
 
162
                               std::to_string(args.size()) +
 
163
                               " expected " +
 
164
                               std::to_string(m_spec_args.size()));
 
165
         }
 
166
 
 
167
      m_user_args.insert(std::make_pair(arg, args[arg_i]));
 
168
 
 
169
      if(args[arg_i] == "-")
 
170
         {
 
171
         if(seen_stdin_flag)
 
172
            {
 
173
            throw CLI_Usage_Error("Cannot specify '-' (stdin) more than once");
 
174
            }
 
175
         seen_stdin_flag = true;
 
176
         }
 
177
 
 
178
      ++arg_i;
 
179
      }
 
180
 
 
181
   if(m_spec_rest.empty())
 
182
      {
 
183
      if(arg_i != args.size())
 
184
         {
 
185
         throw CLI_Usage_Error("Too many arguments");
 
186
         }
 
187
      }
 
188
   else
 
189
      {
 
190
      m_user_rest.assign(args.begin() + arg_i, args.end());
 
191
      }
 
192
 
 
193
   // Now insert any defaults for options not supplied by the user
 
194
   for(auto const& opt : m_spec_opts)
 
195
      {
 
196
      if(m_user_args.count(opt.first) == 0)
 
197
         {
 
198
         m_user_args.insert(opt);
 
199
         }
 
200
      }
 
201
   }
 
202
 
 
203
Argument_Parser::Argument_Parser(const std::string& spec,
 
204
                                 const std::vector<std::string>& extra_flags,
 
205
                                 const std::vector<std::string>& extra_opts)
 
206
   {
 
207
   class CLI_Error_Invalid_Spec : public CLI_Error
 
208
      {
 
209
      public:
 
210
         explicit CLI_Error_Invalid_Spec(const std::string& bad_spec)
 
211
            : CLI_Error("Invalid command spec '" + bad_spec + "'") {}
 
212
      };
 
213
 
 
214
   const std::vector<std::string> parts = Botan::split_on(spec, ' ');
 
215
 
 
216
   if(parts.size() == 0)
 
217
      {
 
218
      throw CLI_Error_Invalid_Spec(spec);
 
219
      }
 
220
 
 
221
   for(size_t i = 1; i != parts.size(); ++i)
 
222
      {
 
223
      const std::string s = parts[i];
 
224
 
 
225
      if(s.empty()) // ?!? (shouldn't happen)
 
226
         {
 
227
         throw CLI_Error_Invalid_Spec(spec);
 
228
         }
 
229
 
 
230
      if(s.size() > 2 && s[0] == '-' && s[1] == '-')
 
231
         {
 
232
         // option or flag
 
233
 
 
234
         auto eq = s.find('=');
 
235
 
 
236
         if(eq == std::string::npos)
 
237
            {
 
238
            m_spec_flags.insert(s.substr(2, std::string::npos));
 
239
            }
 
240
         else
 
241
            {
 
242
            m_spec_opts.insert(std::make_pair(s.substr(2, eq - 2), s.substr(eq + 1, std::string::npos)));
 
243
            }
 
244
         }
 
245
      else if(s[0] == '*')
 
246
         {
 
247
         // rest argument
 
248
         if(m_spec_rest.empty() && s.size() > 2)
 
249
            {
 
250
            m_spec_rest = s.substr(1, std::string::npos);
 
251
            }
 
252
         else
 
253
            {
 
254
            throw CLI_Error_Invalid_Spec(spec);
 
255
            }
 
256
         }
 
257
      else
 
258
         {
 
259
         // named argument
 
260
         if(!m_spec_rest.empty()) // rest arg wasn't last
 
261
            {
 
262
            throw CLI_Error_Invalid_Spec(spec);
 
263
            }
 
264
 
 
265
         m_spec_args.push_back(s);
 
266
         }
 
267
      }
 
268
 
 
269
   for(std::string flag : extra_flags)
 
270
      m_spec_flags.insert(flag);
 
271
   for(std::string opt : extra_opts)
 
272
      m_spec_opts.insert(std::make_pair(opt, ""));
 
273
   }
 
274
 
 
275
 
 
276
}
 
277
 
 
278
#endif