104 lines
3.5 KiB
C++
104 lines
3.5 KiB
C++
|
#include <cstring>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include "database.h"
|
||
|
#include "utils.h"
|
||
|
|
||
|
static std::filesystem::path normalize(std::filesystem::path path);
|
||
|
static std::optional<std::filesystem::path> relative_path_from(const std::filesystem::path& from, const std::filesystem::path& path);
|
||
|
|
||
|
void output_meme(const char* path, const char* source, const char* description, const char* miscinfo, int fd, bool edit) {
|
||
|
auto output = [=](const char* header, const char* contents, bool end = false) {
|
||
|
write(fd, "# ", 2);
|
||
|
write(fd, header, strlen(header));
|
||
|
write(fd, ":\n", 2);
|
||
|
write(fd, contents, strlen(contents));
|
||
|
write(fd, "\n", 1);
|
||
|
if (!end) {
|
||
|
write(fd, edit ? "\n\0\n" : "\n", edit ? 3 : 1);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
output(edit ? "Path (immutable)" : "Path", path);
|
||
|
output("Source", source);
|
||
|
output("Description", description);
|
||
|
output("Miscellaneous information", miscinfo, true);
|
||
|
}
|
||
|
|
||
|
std::vector<std::filesystem::path> get_paths_relative_to_database(const blankie::cliparse::Parser& parser, const Database& db, int* rc, bool needs_exist) {
|
||
|
std::vector<std::filesystem::path> paths;
|
||
|
|
||
|
paths.reserve(parser.arguments.size() - 1);
|
||
|
for (size_t i = 1; i < parser.arguments.size(); i++) {
|
||
|
std::filesystem::path normalized_path = normalize(parser.arguments[i]);
|
||
|
std::optional<std::filesystem::path> relative_path = relative_path_from(db.path.parent_path(), normalized_path);
|
||
|
if (!relative_path) {
|
||
|
fprintf(stderr, "%s is not in %s\n", normalized_path.c_str(), db.path.parent_path().c_str());
|
||
|
*rc = 1;
|
||
|
} else if (needs_exist && !std::filesystem::is_regular_file(normalized_path)) {
|
||
|
fprintf(stderr, "%s is not a file\n", normalized_path.c_str());
|
||
|
*rc = 1;
|
||
|
} else {
|
||
|
paths.push_back(*relative_path);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return paths;
|
||
|
}
|
||
|
|
||
|
std::filesystem::path get_path_relative_to_database(const char* path, const Database& db) {
|
||
|
std::filesystem::path normalized_path = normalize(path);
|
||
|
std::optional<std::filesystem::path> relative_path = relative_path_from(db.path.parent_path(), normalized_path);
|
||
|
|
||
|
if (!relative_path) {
|
||
|
fprintf(stderr, "%s is not in %s\n", normalized_path.c_str(), db.path.parent_path().c_str());
|
||
|
exit(1);
|
||
|
}
|
||
|
if (!std::filesystem::is_regular_file(normalized_path)) {
|
||
|
fprintf(stderr, "%s is not a file\n", normalized_path.c_str());
|
||
|
exit(1);
|
||
|
}
|
||
|
return *relative_path;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
std::filesystem::path normalize(std::filesystem::path path) {
|
||
|
if (path.is_relative()) {
|
||
|
path = std::filesystem::current_path() / path;
|
||
|
}
|
||
|
|
||
|
std::filesystem::path normalized = path.root_path();
|
||
|
for (auto it = path.begin(); it != path.end(); it++) {
|
||
|
if (*it == ".") {
|
||
|
// do nothing
|
||
|
} else if (*it == "..") {
|
||
|
normalized = normalized.parent_path();
|
||
|
} else {
|
||
|
normalized /= *it;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return normalized;
|
||
|
}
|
||
|
|
||
|
std::optional<std::filesystem::path> relative_path_from(const std::filesystem::path& from, const std::filesystem::path& path) {
|
||
|
auto from_it = from.begin();
|
||
|
auto path_it = path.begin();
|
||
|
|
||
|
for (; from_it != from.end() && path_it != path.end(); from_it++, path_it++) {
|
||
|
if (*from_it != *path_it) {
|
||
|
return std::nullopt;
|
||
|
}
|
||
|
}
|
||
|
if (from_it != from.end()) {
|
||
|
return std::nullopt;
|
||
|
}
|
||
|
|
||
|
std::filesystem::path name;
|
||
|
for (; path_it != path.end(); path_it++) {
|
||
|
name /= *path_it;
|
||
|
}
|
||
|
return name;
|
||
|
}
|