#include "options.hpp"
#include "libmemcached/common.h"
+#include <cerrno>
+#include <cstring>
#include <fstream>
bool check_buffering(const client_options &opt, memcached_st &memc) {
if (opt.isset("debug")) {
std::cerr << "Opening '" << file << "' for writing.\n";
}
- stream.open(file);
+ errno = 0;
+ stream.open(file, std::ios::binary | std::ios::out);
if (stream.is_open()) {
return &stream;
} else if (!opt.isset("quiet")) {
- std::cerr << "Failed to open '" << file << "' for writing.\n";
+ std::cerr << "Failed to open '" << file << "' for writing: " << strerror(errno) << ".\n";
}
}
return &std::cout;
}
+
+std::istream *check_istream(const client_options &opt, const char *file, std::ifstream &stream) {
+ if (file && *file) {
+ if (file[0] != '-' || file[1] != 0) {
+ if (opt.isset("debug")) {
+ std::cerr << "Opening '" << file << "' for reading.\n";
+ }
+ errno = 0;
+ stream.open(file, std::ios::binary | std::ios::in);
+ if (stream.is_open()) {
+ return &stream;
+ } else if (!opt.isset("quiet")) {
+ std::cerr << "Failed to open '" << file << "' for reading: " << strerror(errno) << ".\n";
+ }
+ return nullptr;
+ }
+ }
+ return &std::cin;
+}
}
#endif // _WIN32
+ extended_option *servers = nullptr;
for (auto &opt : options) {
if (opt.apply) {
+ // servers should be applied last, so they take up any behaviors previously set
+ if (opt.opt.val == 's' && opt.opt.name == std::string("servers")) {
+ servers = &opt;
+ continue;
+ }
if (!opt.apply(*this, opt, memc)) {
return false;
}
}
}
+ if (servers) {
+ if (!servers->apply(*this, *servers, memc)) {
+ return false;
+ }
+ }
return true;
}
std::string help;
std::function<bool(client_options &, extended_option &)> parse;
std::function<bool(const client_options &, const extended_option &, memcached_st *)> apply;
- const char *arg;
+ char *arg;
bool set;
};
extended_option &get(const std::string &name) {
// UB if not found
- return *std::find_if(options.begin(), options.end(), [&name](extended_option &ext) {
- return ext.opt.name && ext.opt.name == name;
- });
+ return *find(name);
}
extended_option &get(int c) {
// UB if not found
- return *std::find_if(options.begin(), options.end(), [c](extended_option &ext) {
- return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
- });
+ return *find(c);
}
const extended_option &get(const std::string &name) const {
- for (const auto &ext_opt : options) {
- if (ext_opt.opt.name && ext_opt.opt.name == name) {
- return ext_opt;
- }
- }
- return null_ext_opt;
+ // UB if not found
+ return *find(name);
}
const extended_option &get(int c) const {
- for (const auto &ext_opt : options) {
- if (ext_opt.opt.val == c) {
- return ext_opt;
- } else if (c == 1 && ext_opt.opt.val == '-') {
- // GNU argv extension
- return ext_opt;
- }
- }
- return null_ext_opt;
+ // UB if not found
+ return *find(c);
+ }
+
+ bool has(const std::string &name) const {
+ auto found = find(name);
+ return found != options.cend();
+ }
+ bool has(int c) const {
+ auto found = find(c);
+ return found != options.cend();
}
bool isset(const std::string &name) const {
- return get(name).set;
+ return has(name) && get(name).set;
}
bool isset(int c) const {
- return get(c).set;
+ return has(c) && get(c).set;
}
void unset(const std::string &name) {
- auto &opt = get(name);
- opt.set = false;
- opt.arg = nullptr;
+ set(name, false);
}
void unset(int c) {
- auto &opt = get(c);
- opt.set = false;
- opt.arg = nullptr;
+ set(c, false);
}
- void set(const std::string &name, bool set_ = true, const char *optarg_ = nullptr) {
- auto &opt = get(name);
- opt.set = set_;
- opt.arg = optarg_;
+ void set(const std::string &name, bool set_ = true, char *optarg_ = nullptr) {
+ if (has(name)) {
+ auto &opt = get(name);
+ opt.set = set_;
+ opt.arg = optarg_;
+ }
}
- void set(int c, bool set_ = true, const char *optarg_ = nullptr) {
- auto &opt = get(c);
- opt.set = set_;
- opt.arg = optarg_;
+ void set(int c, bool set_ = true, char *optarg_ = nullptr) {
+ if (has(c)) {
+ auto &opt = get(c);
+ opt.set = set_;
+ opt.arg = optarg_;
+ }
}
const char *argof(const std::string &name) const {
- return get(name).arg;
+ if (has(name)) {
+ return get(name).arg;
+ }
+ return nullptr;
}
const char *argof(int c) const {
- return get(c).arg;
+ if (has(c)) {
+ return get(c).arg;
+ }
+ return nullptr;
}
const extended_option &operator[](const std::string &name) const {
bool apply(memcached_st *memc);
private:
+ using iterator = std::vector<extended_option>::iterator;
+ using const_iterator = std::vector<extended_option>::const_iterator;
+ using predicate = std::function<bool(const extended_option &ext)>;
+
static option null_opt;
static const extended_option null_ext_opt;
+
+ const_iterator find(const predicate &pred) const {
+ return std::find_if(options.cbegin(), options.cend(), pred);
+ }
+ const_iterator find(const std::string &name) const {
+ return find([&name](const extended_option &ext) {
+ return ext.opt.name && ext.opt.name == name;
+ });
+ }
+ const_iterator find(int c) const {
+ return find([c](const extended_option &ext) {
+ return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
+ });
+ }
+
+ iterator find(const predicate &pred) {
+ return std::find_if(options.begin(), options.end(), pred);
+ }
+ iterator find(const std::string &name) {
+ return find([&name](const extended_option &ext) {
+ return ext.opt.name && ext.opt.name == name;
+ });
+ }
+ iterator find(int c) {
+ return find([c](const extended_option &ext) {
+ return ext.opt.val == c || (c == 1 && ext.opt.val == '-');
+ });
+ }
};
#include "common/options.hpp"
#include "common/checks.hpp"
+#include <cerrno>
#include <climits>
#include <cstdlib>
#include <libgen.h>
ADD,
REPLACE
} op;
- const char *path;
+ char *path;
uint32_t flags;
time_t expire;
};
-static void add_file(std::vector<memcp_file> &files, const client_options &opt, const char *file) {
+static inline std::string stream2string(const std::istream &istream) {
+ return dynamic_cast<std::ostringstream &>(std::ostringstream{} << istream.rdbuf()).str();
+}
+
+static memcached_return_t memcp(const client_options &opt, memcached_st &memc, const char *key,
+ const memcp_file &file) {
+ std::ifstream fstream{};
+ std::istream *istream = check_istream(opt, file.path, fstream);
+
+ if (!istream){
+ return MEMCACHED_ERROR;
+ }
+
+ const char *mode;
+ memcached_return_t rc;
+ auto data = stream2string(*istream);
+ if (file.op == memcp_file::mode::REPLACE) {
+ mode = "replace";
+ rc = memcached_replace(&memc, key, strlen(key), data.c_str(), data.length(),
+ file.expire, file.flags);
+ } else if (file.op == memcp_file::mode::ADD) {
+ mode = "add";
+ rc = memcached_add(&memc, key, strlen(key), data.c_str(), data.length(),
+ file.expire, file.flags);
+ } else {
+ mode = "set";
+ rc = memcached_set(&memc, key, strlen(key), data.c_str(), data.length(),
+ file.expire, file.flags);
+ }
+
+ if (!memcached_success(rc)) {
+ auto error = memcached_last_error(&memc)
+ ? memcached_last_error_message(&memc)
+ : memcached_strerror(&memc, rc);
+ std::cerr << "Error occurred during memcached_" << mode <<"('" << key << "'): " << error << "\n";
+ }
+ return rc;
+}
+
+static void add_file(std::vector<memcp_file> &files, const client_options &opt, char *file) {
memcp_file::type type = memcp_file::type::basename;
memcp_file::mode mode = memcp_file::mode::SET;
uint32_t flags = 0;
files.emplace_back(memcp_file{type, mode, file, flags, expire});
}
+static bool path2key(const client_options &opt, memcp_file &file, char **path) {
+ static char rpath[PATH_MAX + 1];
+ if (file.key == memcp_file::type::absolute) {
+ *path = realpath(file.path, rpath);
+ if (!*path) {
+ if (!opt.isset("quiet")) {
+ perror(file.path);
+ }
+ return false;
+ }
+ } else if (file.key == memcp_file::type::relative) {
+ *path = file.path;
+ } else {
+ *path = basename((file.path));
+ }
+ return true;
+}
+
int main(int argc, char *argv[]) {
std::vector<memcp_file> files{};
- client_options opt{PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_DESCRIPTION, "file [file ...]"};
+ client_options opt{PROGRAM_NAME, PROGRAM_VERSION, PROGRAM_DESCRIPTION,
+ "file [file ...]"
+ "\n\t\t\t# NOTE: order of flags and positional"
+ "\n\t\t\t# arguments matters on GNU systems)"};
opt.add(nullptr, '-', no_argument, "GNU argv extension")
.parse = [&files](client_options &opt_, client_options::extended_option &ext) {
}
auto exit_code = EXIT_SUCCESS;
- for (const auto &file : files) {
- auto filename = file.path;
- std::ifstream filestream{filename, std::ios::in|std::ios::binary};
-
- if (!filestream) {
- if (!opt.isset("quiet")) {
- std::cerr << "Could not open file '" << filename << "'.\n";
- }
+ for (auto &file : files) {
+ char *path = nullptr;
+ if (!path2key(opt, file, &path)) {
exit_code = EXIT_FAILURE;
- // continue;
- } else {
- const char *path;
- char rpath[PATH_MAX+1];
-
- if (file.key == memcp_file::type::relative) {
- path = filename;
- } else if (file.key == memcp_file::type::absolute) {
- path = realpath(filename, rpath);
- if (!path) {
- if (!opt.isset("quiet")) {
- perror(filename);
- }
- exit_code = EXIT_FAILURE;
- continue;
- }
- } else {
- path = basename(const_cast<char *>(filename));
- }
-
- std::ostringstream data{};
- data << filestream.rdbuf();
-
- memcached_return_t rc;
- const char *mode;
- if (file.op == memcp_file::mode::REPLACE) {
- mode = "replace";
- rc = memcached_replace(&memc, path, strlen(path), data.str().c_str(), data.str().length(),
- file.expire, file.flags);
- } else if (file.op == memcp_file::mode::ADD) {
- mode = "add";
- rc = memcached_add(&memc, path, strlen(path), data.str().c_str(), data.str().length(),
- file.expire, file.flags);
- } else {
- mode = "set";
- rc = memcached_set(&memc, path, strlen(path), data.str().c_str(), data.str().length(),
- file.expire, file.flags);
- }
-
- if (!memcached_success(rc)) {
- exit_code = EXIT_FAILURE;
-
- auto error = memcached_last_error(&memc)
- ? memcached_last_error_message(&memc)
- : memcached_strerror(&memc, rc);
- std::cerr << "Error occurred during memcached_" << mode <<"('" << path << "'): " << error << "\n";
- break;
- }
+ continue;
+ }
+ auto rc = memcp(opt, memc, path, file);
+ if (memcached_success(rc)) {
if (opt.isset("verbose")) {
std::cout << path << "\n";
}
+ } else {
+ exit_code = EXIT_FAILURE;
}
}
SECTION("no servers provided") {
string output;
REQUIRE_FALSE(sh.run("memcp nonexistent", output));
- REQUIRE(output == "No Servers provided\n");
+ REQUIRE(output == "No servers provided.\n");
}
SECTION("--help") {
string output;
REQUIRE(sh.run("memcp --help", output));
- REQUIRE_THAT(output, Contains("memcp"));
- REQUIRE_THAT(output, Contains("v1"));
- REQUIRE_THAT(output, Contains("help"));
- REQUIRE_THAT(output, Contains("version"));
- REQUIRE_THAT(output, Contains("option"));
- REQUIRE_THAT(output, Contains("--"));
- REQUIRE_THAT(output, Contains("="));
+ REQUIRE_THAT(output, Contains("memcp v1"));
+ REQUIRE_THAT(output, Contains("Usage:"));
+ REQUIRE_THAT(output, Contains("file [file ...]"));
+ REQUIRE_THAT(output, Contains("Options:"));
+ REQUIRE_THAT(output, Contains("-h|--help"));
+ REQUIRE_THAT(output, Contains("-V|--version"));
+ REQUIRE_THAT(output, Contains("Environment:"));
+ REQUIRE_THAT(output, Contains("MEMCACHED_SERVERS"));
}
SECTION("with server") {