#define PROGRAM_VERSION "1.1"
#include "common/options.hpp"
+#include "common/checks.hpp"
+#include "p9y/libgen.hpp"
+#include "p9y/realpath.hpp"
+#include <cerrno>
#include <climits>
#include <cstdlib>
#include <fstream>
#include <sstream>
+#ifndef PATH_MAX
+# ifdef MAX_PATH
+# define PATH_MAX MAX_PATH
+# else
+# define PATH_MAX 256
+# endif
+#endif
+
struct memcp_file {
- enum key {
+ enum class type {
basename,
relative,
absolute
- } type;
- enum op {
+ } key;
+ enum class mode {
SET,
ADD,
REPLACE
- } mode;
- const char *path;
+ } op;
+ char *path;
uint32_t flags;
time_t expire;
};
-static void add_file(std::vector<memcp_file> &files, const client_options &opt, const char *file) {
- memcp_file::key type = memcp_file::basename;
- memcp_file::op mode = memcp_file::SET;
+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;
time_t expire = 0;
if (opt.isset("absolute")) {
- type = memcp_file::absolute;
+ type = memcp_file::type::absolute;
} else if (opt.isset("relative")) {
- type = memcp_file::relative;
+ type = memcp_file::type::relative;
}
if (opt.isset("replace")) {
- mode = memcp_file::REPLACE;
+ mode = memcp_file::mode::REPLACE;
} else if (opt.isset("add")) {
- mode = memcp_file::ADD;
+ mode = memcp_file::mode::ADD;
}
if (auto flags_str = opt.argof("flags")) {
}
if (opt.isset("debug")) {
- auto mode_str = mode == memcp_file::REPLACE ? "REPLACE" : mode == memcp_file::ADD ? "ADD" : "SET";
+ auto mode_str = mode == memcp_file::mode::REPLACE ? "REPLACE" : mode == memcp_file::mode::ADD ? "ADD" : "SET";
std::cerr << "Scheduling " << mode_str << " '" << file << "' (expire=" << expire << ", flags=" << flags << ").\n";
}
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[]) {
- memcached_st memc;
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) {
};
opt.add("udp", 'U', no_argument, "Use UDP.")
- .apply = [](const client_options &opt, const client_options::extended_option &ext, memcached_st *memc) {
+ .apply = [](const client_options &opt_, const client_options::extended_option &ext, memcached_st *memc) {
if (MEMCACHED_SUCCESS != memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_USE_UDP, ext.set)) {
- if (!opt.isset("quiet")) {
+ if (!opt_.isset("quiet")) {
std::cerr << memcached_last_error_message(memc) << "\n";
}
return false;
exit(EXIT_FAILURE);
}
- if (!memcached_create(&memc)) {
- if (!opt.isset("quiet")) {
- std::cerr << "Failed to initialize memcached client.\n";
- }
+ memcached_st memc;
+ if (!check_memcached(opt, memc)) {
exit(EXIT_FAILURE);
}
}
if (files.empty()) {
- if (!*argp) {
- if (!opt.isset("quiet")) {
- std::cerr << "No file(s) provided.\n";
- }
+ if (!check_argp(opt, argp, "No file(s) provided.")) {
memcached_free(&memc);
exit(EXIT_FAILURE);
}
}
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.type == memcp_file::relative) {
- path = filename;
- } else if (file.type == memcp_file::absolute) {
- path = realpath(filename, rpath);
- if (!path) {
- if (!opt.isset("quiet")) {
- perror(filename);
- }
- exit_code = EXIT_FAILURE;
- continue;
- }
- } else {
- path = basename(filename);
- }
-
- std::ostringstream data{};
- data << filestream.rdbuf();
-
- memcached_return_t rc;
- const char *mode;
- if (file.mode == memcp_file::REPLACE) {
- mode = "replace";
- rc = memcached_replace(&memc, path, strlen(path), data.str().c_str(), data.str().length(),
- file.expire, file.flags);
- } else if (file.mode == memcp_file::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::cerr << path << "\n";
+ std::cout << path << "\n";
}
+ } else {
+ exit_code = EXIT_FAILURE;
}
}
+ if (!check_buffering(opt, memc)) {
+ exit_code = EXIT_FAILURE;
+ }
+
memcached_free(&memc);
exit(exit_code);
}