980ca38dc39ab8865a498002e98d6f334d4c2814
[m6w6/libmemcached] / src / bin / common / options.hpp
1 /*
2 +--------------------------------------------------------------------+
3 | libmemcached - C/C++ Client Library for memcached |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted under the terms of the BSD license. |
7 | You should have received a copy of the license in a bundled file |
8 | named LICENSE; in case you did not receive a copy you can review |
9 | the terms online at: https://opensource.org/licenses/BSD-3-Clause |
10 +--------------------------------------------------------------------+
11 | Copyright (c) 2006-2014 Brian Aker https://datadifferential.com/ |
12 | Copyright (c) 2020 Michael Wallner <mike@php.net> |
13 +--------------------------------------------------------------------+
14 */
15
16 #pragma once
17
18 #include <algorithm>
19 #include <cstdint>
20 #include <climits>
21 #include <functional>
22 #include <iostream>
23 #include <string>
24 #include <vector>
25
26 #include "libmemcached/common.h"
27
28 #ifdef HAVE_GETOPT_H
29 # include <getopt.h>
30 #elif defined _MSC_VER
31 # include "win32/getopt.h"
32 #endif
33
34 class client_options {
35 public:
36
37 struct extended_option {
38 option opt;
39 std::string help;
40 std::function<bool(client_options &, extended_option &)> parse;
41 std::function<bool(const client_options &, const extended_option &, memcached_st *)> apply;
42 char *arg;
43 bool set;
44 };
45
46 std::vector<extended_option> options;
47 std::vector<extended_option> defaults;
48
49 const char *prog_name;
50 const char *prog_vers;
51 const char *prog_desc;
52 const char *prog_argp;
53
54 client_options(const char *prg, const char *ver, const char *dsc, const char *arg = nullptr)
55 : options{}
56 , defaults{}
57 , prog_name{prg}
58 , prog_vers{ver}
59 , prog_desc{dsc}
60 , prog_argp{arg}
61 {
62
63 def("help", 'h', no_argument, "Print this help.")
64 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *) {
65 if (ext.set) {
66 opt.print_help();
67 exit(EXIT_SUCCESS);
68 }
69 return true;
70 };
71 def("version", 'V', no_argument, "Print program version.")
72 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *) {
73 if (ext.set) {
74 opt.print_version();
75 exit(EXIT_SUCCESS);
76 }
77 return true;
78 };
79
80 def("verbose", 'v', no_argument, "Print more informational output.")
81 .parse = [](client_options &opt, extended_option &) {
82 opt.unset("quiet");
83 return true;
84 };
85 def("debug", 'd', no_argument, "Print output useful only for debugging.")
86 .parse = [](client_options &opt, extended_option &) {
87 opt.set("verbose");
88 opt.unset("quiet");
89 return true;
90 };
91 def("quiet", 'q', no_argument, "Print no output, not even errors.")
92 .parse = [](client_options &opt, extended_option &) {
93 opt.unset("verbose");
94 opt.unset("debug");
95 return true;
96 };
97
98 def("password", 'p', required_argument, "SASL password.");
99 def("username", 'u', required_argument, "SASL username.")
100 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
101 if (auto username = ext.arg) {
102 #if !LIBMEMCACHED_WITH_SASL_SUPPORT
103 if (!opt.isset("quiet")) {
104 std::cerr
105 << "SASL username was supplied, but binary was not built with SASL support.\n";
106 }
107 return false;
108 #else
109 if (memc) {
110 if (MEMCACHED_SUCCESS
111 != memcached_set_sasl_auth_data(memc, username, opt.argof("password"))) {
112 if (!opt.isset("quiet")) {
113 std::cerr << memcached_last_error_message(memc);
114 }
115 return false;
116 }
117 }
118 #endif
119 }
120 return true;
121 };
122
123 def("binary", 'b', no_argument, "Use the binary memcached protocol.")
124 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
125 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, ext.set)) {
126 if(!opt.isset("quiet")) {
127 std::cerr << memcached_last_error_message(memc);
128 }
129 return false;
130 }
131 return true;
132 };
133 def("buffer", 'B', no_argument, "Buffer requests.")
134 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
135 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BUFFER_REQUESTS, ext.set)) {
136 if(!opt.isset("quiet")) {
137 std::cerr << memcached_last_error_message(memc);
138 }
139 return false;
140 }
141 return true;
142 };
143 def("non-blocking", 'n', no_argument, "Use non-blocking connections.")
144 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
145 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, ext.set)) {
146 if(!opt.isset("quiet")) {
147 std::cerr << memcached_last_error_message(memc);
148 }
149 return false;
150 }
151 return true;
152 };
153 def("tcp-nodelay", 'N', no_argument, "Disable Nagle's algorithm.")
154 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
155 if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_TCP_NODELAY, ext.set)) {
156 if(!opt.isset("quiet")) {
157 std::cerr << memcached_last_error_message(memc);
158 }
159 return false;
160 }
161 return true;
162 };
163 def("servers", 's', required_argument, "List of servers to connect to.")
164 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
165 auto servers = ext.arg;
166 if (!servers) {
167 if (opt.isset("verbose")) {
168 std::cerr << "Checking environment for a server list in MEMCACHED_SERVERS.\n";
169 }
170 servers = getenv("MEMCACHED_SERVERS");
171 if (!servers || !*servers) {
172 if (!opt.isset("quiet")) {
173 std::cerr << "No servers provided.\n";
174 }
175 return false;
176 }
177 }
178
179 auto server_list = memcached_servers_parse(servers);
180 if (!server_list || !memcached_server_list_count(server_list)) {
181 if (!opt.isset("quiet")) {
182 std::cerr << "Invalid server list provided: '" << servers << "'\n";
183 }
184 if (server_list) {
185 memcached_server_list_free(server_list);
186 }
187 return false;
188 }
189
190 if (MEMCACHED_SUCCESS != memcached_server_push(memc, server_list)) {
191 if (!opt.isset("quiet")) {
192 std::cerr << memcached_last_error_message(memc);
193 }
194 memcached_server_list_free(server_list);
195 return false;
196 }
197 memcached_server_list_free(server_list);
198 return true;
199 };
200 def("hash", 'H', required_argument, "Key hashing method.")
201 .apply = [](const client_options &opt, const extended_option &ext, memcached_st *memc) {
202 if (ext.set) {
203 std::string hash_wanted{ext.arg};
204 memcached_hash_t hash = MEMCACHED_HASH_DEFAULT;
205
206 std::transform(hash_wanted.begin(), hash_wanted.end(), hash_wanted.begin(), ::toupper);
207
208 if (opt.isset("verbose")) {
209 std::cerr << "Checking for hash '" << hash_wanted << "'.\n";
210 }
211 for (int h = MEMCACHED_HASH_DEFAULT; h < MEMCACHED_HASH_MAX; ++h) {
212 auto hash_type = static_cast<memcached_hash_t>(h);
213 std::string hash_string{libmemcached_string_hash(hash_type)};
214
215 if (hash_wanted.length() == hash_string.length()) {
216 auto ci = std::equal(hash_string.begin(), hash_string.end(), hash_wanted.begin(),
217 [](int a, int b) { return ::toupper(a) == b; });
218 if (ci) {
219 hash = hash_type;
220 break;
221 }
222 }
223 }
224 if (hash == MEMCACHED_HASH_DEFAULT) {
225 if (!opt.isset("quiet")) {
226 std::cerr << "Could not find hash '" << hash_wanted << "'.\n";
227 }
228 }
229 if (MEMCACHED_SUCCESS != memcached_behavior_set_key_hash(memc, hash)) {
230 if (!opt.isset("quiet")) {
231 std::cerr << memcached_last_error_message(memc);
232 }
233 return false;
234 }
235 }
236 return true;
237 };
238 }
239
240 extended_option &def(option opt, std::string help) {
241 defaults.emplace_back(extended_option{opt, std::move(help), {}, {}, nullptr, false});
242 return defaults.back();
243 }
244
245 extended_option &def(const char *name, char flag, int has_arg, const char *help) {
246 return def(option{name, has_arg, nullptr, flag}, help);
247 }
248
249 extended_option &add(extended_option ext) {
250 options.emplace_back(std::move(ext));
251 return options.back();
252 }
253
254 extended_option &add(option opt, std::string help) {
255 options.emplace_back(extended_option{opt, std::move(help), nullptr, nullptr, nullptr, false});
256 return options.back();
257 }
258
259 extended_option &add(const char *name, char flag, int has_arg, const char *help) {
260 return add(option{name, has_arg, nullptr, flag}, help);
261 }
262
263 extended_option &get(const std::string &name) {
264 // UB if not found
265 return *find(name);
266 }
267 extended_option &get(int c) {
268 // UB if not found
269 return *find(c);
270 }
271
272 const extended_option &get(const std::string &name) const {
273 // UB if not found
274 return *find(name);
275 }
276 const extended_option &get(int c) const {
277 // UB if not found
278 return *find(c);
279 }
280
281 bool has(const std::string &name) const {
282 auto found = find(name);
283 return found != options.cend();
284 }
285 bool has(int c) const {
286 auto found = find(c);
287 return found != options.cend();
288 }
289
290 bool isset(const std::string &name) const {
291 return has(name) && get(name).set;
292 }
293 bool isset(int c) const {
294 return has(c) && get(c).set;
295 }
296
297 void unset(const std::string &name) {
298 set(name, false);
299 }
300 void unset(int c) {
301 set(c, false);
302 }
303
304 void set(const std::string &name, bool set_ = true, char *optarg_ = nullptr) {
305 if (has(name)) {
306 auto &opt = get(name);
307 opt.set = set_;
308 opt.arg = optarg_;
309 }
310 }
311 void set(int c, bool set_ = true, char *optarg_ = nullptr) {
312 if (has(c)) {
313 auto &opt = get(c);
314 opt.set = set_;
315 opt.arg = optarg_;
316 }
317 }
318
319 char *argof(const std::string &name) const {
320 if (has(name)) {
321 return get(name).arg;
322 }
323 return nullptr;
324 }
325 char *argof(int c) const {
326 if (has(c)) {
327 return get(c).arg;
328 }
329 return nullptr;
330 }
331
332 const extended_option &operator[](const std::string &name) const {
333 return get(name);
334 }
335 const extended_option &operator[](int c) const {
336 return get(c);
337 }
338
339 void print_version() const;
340 void print_help() const;
341
342 bool parse(int argc, char *argv[], char ***argp = nullptr);
343 bool apply(memcached_st *memc);
344
345 private:
346 using iterator = std::vector<extended_option>::iterator;
347 using const_iterator = std::vector<extended_option>::const_iterator;
348 using predicate = std::function<bool(const extended_option &ext)>;
349
350 const_iterator find(const predicate &pred) const {
351 return std::find_if(options.cbegin(), options.cend(), pred);
352 }
353 const_iterator find(const std::string &name) const {
354 return find([&name](const extended_option &ext) {
355 return ext.opt.name && ext.opt.name == name;
356 });
357 }
358 const_iterator find(int c) const {
359 return find([c](const extended_option &ext) {
360 return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
361 });
362 }
363
364 iterator find(const predicate &pred) {
365 return std::find_if(options.begin(), options.end(), pred);
366 }
367 iterator find(const std::string &name) {
368 return find([&name](const extended_option &ext) {
369 return ext.opt.name && ext.opt.name == name;
370 });
371 }
372 iterator find(int c) {
373 return find([c](const extended_option &ext) {
374 return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
375 });
376 }
377 };