--- a/nbproject/configurations.xml Sun Jan 13 16:44:11 2019 +0100
+++ b/nbproject/configurations.xml Wed Jan 16 17:23:05 2019 +0100
@@ -42,6 +42,12 @@
<logicalFolder name="root" displayName="root" projectFiles="true" kind="ROOT">
<df root="." name="0">
<df name="src">
+ <in>AttributeFinder.h</in>
+ <in>CLIParser.h</in>
+ <in>Configuration.h</in>
+ <in>FileAttributeFinder.h</in>
+ <in>RequestedField.h</in>
+ <in>XattrAttributeFinder.h</in>
<in>relpipe-in-filesystem.cpp</in>
</df>
</df>
@@ -91,6 +97,18 @@
<preBuildFirst>true</preBuildFirst>
</preBuild>
</makefileType>
+ <item path="src/AttributeFinder.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/CLIParser.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/Configuration.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/FileAttributeFinder.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/RequestedField.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/XattrAttributeFinder.h" ex="false" tool="3" flavor2="0">
+ </item>
<item path="src/relpipe-in-filesystem.cpp" ex="false" tool="1" flavor2="0">
<ccTool flags="0">
</ccTool>
@@ -130,6 +148,18 @@
<preBuildFirst>true</preBuildFirst>
</preBuild>
</makefileType>
+ <item path="src/AttributeFinder.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/CLIParser.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/Configuration.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/FileAttributeFinder.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/RequestedField.h" ex="false" tool="3" flavor2="0">
+ </item>
+ <item path="src/XattrAttributeFinder.h" ex="false" tool="3" flavor2="0">
+ </item>
<item path="src/relpipe-in-filesystem.cpp" ex="false" tool="1" flavor2="0">
<ccTool flags="0">
</ccTool>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/AttributeFinder.h Wed Jan 16 17:23:05 2019 +0100
@@ -0,0 +1,97 @@
+/**
+ * Relational pipes
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+#include <filesystem>
+
+#include <relpipe/writer/typedefs.h>
+#include <relpipe/writer/AttributeMetadata.h>
+#include <relpipe/writer/RelationalWriter.h>
+
+#include "RequestedField.h"
+
+namespace relpipe {
+namespace in {
+namespace filesystem {
+
+namespace fs = std::filesystem;
+using namespace relpipe::writer;
+
+class AttributeFinder {
+public:
+
+ /**
+ * Single requested fields might generate multiple attributes in the relation.
+ * But usually it is 1:1.
+ * @param field requested field from the user (usually from CLI arguments)
+ * @return attribute metadata to be used in the RelationalWriter.startRelation()
+ */
+ virtual vector<AttributeMetadata> toMetadata(const RequestedField& field) = 0;
+
+ /**
+ * Writing of the record for current file is starting.
+ * Following writeField() calls are related to this file.
+ * @param file
+ */
+ virtual void startFile(const fs::path& file) = 0;
+
+ /**
+ * Writing of the record for current file is finished. All resources linked to this file should be released.
+ */
+ virtual void endFile() = 0;
+
+ /**
+ * Writes field attribute(s). The attribute count must match with count of AttributeMetadata returned in toMetadata().
+ * @param writer
+ * @param field
+ */
+ virtual void writeField(RelationalWriter* writer, const RequestedField& field) = 0;
+
+ /**
+ * Writes empty attribute(s) in case of non-existent file or an error.
+ * The attribute count must match with count of AttributeMetadata returned in toMetadata().
+ * @param writer
+ * @param field
+ */
+ virtual void writeEmptyField(RelationalWriter* writer, const RequestedField& field) {
+ // TODO: better handling of null values (when null values are supported by the format specification)
+ for (AttributeMetadata m : toMetadata(field)) {
+ switch (m.typeId) {
+ case TypeId::BOOLEAN:
+ writer->writeAttribute(L"false");
+ break;
+ case TypeId::INTEGER:
+ writer->writeAttribute(L"0");
+ break;
+ case TypeId::STRING:
+ writer->writeAttribute(L"");
+ break;
+ default:
+ throw RelpipeWriterException(L"Unsupported attribute type in writeEmptyField().");
+ }
+ }
+ }
+
+ virtual ~AttributeFinder() {
+ }
+};
+
+}
+}
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/CLIParser.h Wed Jan 16 17:23:05 2019 +0100
@@ -0,0 +1,57 @@
+/**
+ * Relational pipes
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+
+#include <relpipe/writer/typedefs.h>
+
+#include "Configuration.h"
+#include "FileAttributeFinder.h"
+
+namespace relpipe {
+namespace in {
+namespace filesystem {
+
+using namespace relpipe::writer;
+
+class CLIParser {
+public:
+
+ Configuration parse(const std::vector<string_t>& arguments) {
+ Configuration c;
+ // TODO: parse arguments
+ c.fields.push_back(RequestedField(RequestedField::GROUP_FILE, FileAttributeFinder::FIELD_PATH_ORIGINAL));
+ // c.fields.push_back(RequestedField(RequestedField::GROUP_FILE, FileAttributeFinder::FIELD_PATH_ABSOLUTE));
+ // c.fields.push_back(RequestedField(RequestedField::GROUP_FILE, FileAttributeFinder::FIELD_PATH_CANONICAL));
+ // c.fields.push_back(RequestedField(RequestedField::GROUP_FILE, FileAttributeFinder::FIELD_NAME));
+ c.fields.push_back(RequestedField(RequestedField::GROUP_FILE, FileAttributeFinder::FIELD_TYPE));
+ c.fields.push_back(RequestedField(RequestedField::GROUP_FILE, FileAttributeFinder::FIELD_SIZE));
+ c.fields.push_back(RequestedField(RequestedField::GROUP_FILE, FileAttributeFinder::FIELD_OWNER));
+ c.fields.push_back(RequestedField(RequestedField::GROUP_FILE, FileAttributeFinder::FIELD_GROUP));
+ c.fields.push_back(RequestedField(RequestedField::GROUP_XATTR, L"user.xdg.origin.url"));
+ return c;
+ }
+
+ virtual ~CLIParser() {
+ }
+};
+
+}
+}
+}
--- a/src/CMakeLists.txt Sun Jan 13 16:44:11 2019 +0100
+++ b/src/CMakeLists.txt Wed Jan 16 17:23:05 2019 +0100
@@ -1,5 +1,5 @@
# Relational pipes
-# Copyright © 2018 František Kučera (Frantovo.cz, GlobalCode.info)
+# Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/Configuration.h Wed Jan 16 17:23:05 2019 +0100
@@ -0,0 +1,40 @@
+/**
+ * Relational pipes
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+
+#include <relpipe/writer/typedefs.h>
+
+#include "RequestedField.h"
+
+namespace relpipe {
+namespace in {
+namespace filesystem {
+
+class Configuration {
+public:
+ std::vector<RequestedField> fields;
+
+ virtual ~Configuration() {
+ }
+};
+
+}
+}
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/FileAttributeFinder.h Wed Jan 16 17:23:05 2019 +0100
@@ -0,0 +1,144 @@
+/**
+ * Relational pipes
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+#include <filesystem>
+
+#include <relpipe/writer/typedefs.h>
+#include <relpipe/writer/AttributeMetadata.h>
+#include <relpipe/writer/RelationalWriter.h>
+
+#include "RequestedField.h"
+
+namespace relpipe {
+namespace in {
+namespace filesystem {
+
+namespace fs = std::filesystem;
+using namespace relpipe::writer;
+
+class FileAttributeFinder : public AttributeFinder {
+private:
+ std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
+
+ fs::path currentFile;
+ string_t currentOwner;
+ string_t currentGroup;
+
+ string_t getType(const fs::path& file) {
+ // TODO: Use whole words? (letters are compatible with find -type)
+ if (fs::is_regular_file(file)) return L"f";
+ else if (fs::is_symlink(file)) return L"l"; // symlinks to directories are both symlinks and directories
+ else if (fs::is_directory(file)) return L"d";
+ else if (fs::is_fifo(file)) return L"p";
+ else if (fs::is_socket(file)) return L"s";
+ else if (fs::is_block_file(file)) return L"b";
+ else if (fs::is_character_file(file)) return L"c";
+ else return L"o";
+ }
+
+ void fetchOwner(const fs::path& file, string_t& owner, string_t& group) {
+ // TODO: throw exception on error
+ // TODO: get user and group in C++ way?
+ struct stat info;
+ stat(file.c_str(), &info);
+ /**
+ * The return value may point to a static area, and may be
+ * overwritten by subsequent calls to getpwent(3), getpw‐
+ * nam(), or getpwuid(). (Do not pass the returned pointer
+ * to free(3).)
+ */
+ struct passwd* pw = getpwuid(info.st_uid);
+ struct group* gr = getgrgid(info.st_gid);
+ owner = convertor.from_bytes(pw->pw_name);
+ group = convertor.from_bytes(gr->gr_name);
+ }
+public:
+
+ static const string_t FIELD_PATH_ORIGINAL;
+ static const string_t FIELD_NAME;
+ static const string_t FIELD_SIZE;
+ static const string_t FIELD_PATH_ABSOLUTE;
+ static const string_t FIELD_PATH_CANONICAL;
+ static const string_t FIELD_TYPE;
+ static const string_t FIELD_OWNER;
+ static const string_t FIELD_GROUP;
+
+ virtual vector<AttributeMetadata> toMetadata(const RequestedField& field) override {
+ if (field.group == RequestedField::GROUP_FILE) {
+ if (field.name == FIELD_SIZE) return { AttributeMetadata{field.name, TypeId::INTEGER}};
+ else return { AttributeMetadata{field.name, TypeId::STRING}};
+ } else {
+ return {};
+ }
+ }
+
+ void startFile(const fs::path& file) override {
+ currentFile = file;
+ };
+
+ void endFile() override {
+ currentOwner.clear();
+ currentGroup.clear();
+ };
+
+ virtual void writeField(RelationalWriter* writer, const RequestedField& field) override {
+ if (field.group == RequestedField::GROUP_FILE) {
+ if (field.name == FIELD_NAME) {
+ writer->writeAttribute(currentFile.filename().wstring());
+ } else if (field.name == FIELD_PATH_ORIGINAL) {
+ writer->writeAttribute(currentFile.wstring());
+ } else if (field.name == FIELD_PATH_ABSOLUTE) {
+ writer->writeAttribute(fs::absolute(currentFile).wstring());
+ } else if (field.name == FIELD_PATH_CANONICAL) {
+ writer->writeAttribute(fs::canonical(currentFile).wstring());
+ } else if (field.name == FIELD_TYPE) {
+ writer->writeAttribute(getType(currentFile));
+ } else if (field.name == FIELD_SIZE) {
+ integer_t size = fs::is_regular_file(currentFile) ? fs::file_size(currentFile) : 0;
+ writer->writeAttribute(&size, typeid (size));
+ } else if (field.name == FIELD_OWNER) {
+ if (currentOwner.empty()) fetchOwner(currentFile, currentOwner, currentGroup);
+ writer->writeAttribute(currentOwner);
+ } else if (field.name == FIELD_GROUP) {
+ if (currentOwner.empty()) fetchOwner(currentFile, currentOwner, currentGroup);
+ writer->writeAttribute(currentGroup);
+ } else {
+ // TODO: should not happend; check supported attributes in toMetadata()?
+ writer->writeAttribute(L"");
+ }
+ }
+ }
+
+ virtual ~FileAttributeFinder() override {
+ }
+};
+
+const string_t FileAttributeFinder::FIELD_PATH_ORIGINAL = L"path";
+const string_t FileAttributeFinder::FIELD_NAME = L"name";
+const string_t FileAttributeFinder::FIELD_SIZE = L"size";
+const string_t FileAttributeFinder::FIELD_PATH_ABSOLUTE = L"path_absolute";
+const string_t FileAttributeFinder::FIELD_PATH_CANONICAL = L"path_canonical";
+const string_t FileAttributeFinder::FIELD_TYPE = L"type";
+const string_t FileAttributeFinder::FIELD_OWNER = L"owner";
+const string_t FileAttributeFinder::FIELD_GROUP = L"group";
+
+}
+}
+}
--- a/src/FilesystemCommand.h Sun Jan 13 16:44:11 2019 +0100
+++ b/src/FilesystemCommand.h Wed Jan 16 17:23:05 2019 +0100
@@ -1,6 +1,6 @@
/**
* Relational pipes
- * Copyright © 2018 František Kučera (Frantovo.cz, GlobalCode.info)
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,9 +19,10 @@
#include <cstdlib>
#include <iostream>
+#include <sstream>
#include <string>
#include <vector>
-#include <array>
+#include <map>
#include <algorithm>
#include <filesystem>
@@ -33,6 +34,11 @@
#include <relpipe/writer/typedefs.h>
+#include "Configuration.h"
+#include "AttributeFinder.h"
+#include "FileAttributeFinder.h"
+#include "XattrAttributeFinder.h"
+
namespace relpipe {
namespace in {
namespace filesystem {
@@ -41,8 +47,16 @@
using namespace relpipe::writer;
class FilesystemCommand {
+private:
std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
+ FileAttributeFinder fileAttributeFinder;
+ XattrAttributeFinder xattrAttributeFinder;
+
+ std::map<string_t, AttributeFinder*> attributeFinders{
+ {RequestedField::GROUP_FILE, &fileAttributeFinder},
+ {RequestedField::GROUP_XATTR, &xattrAttributeFinder}};
+
void reset(std::stringstream& stream) {
stream.str("");
stream.clear();
@@ -56,81 +70,23 @@
return originalName.tellp();
}
- string_t getType(const fs::path& file) {
- // TODO: Use whole words? (letters are compatible with find -type)
- if (fs::is_regular_file(file)) return L"f";
- else if (fs::is_symlink(file)) return L"l"; // symlinks to directories are both symlinks and directories
- else if (fs::is_directory(file)) return L"d";
- else if (fs::is_fifo(file)) return L"p";
- else if (fs::is_socket(file)) return L"s";
- else if (fs::is_block_file(file)) return L"b";
- else if (fs::is_character_file(file)) return L"c";
- else return L"o";
- }
-
- void fetchOwner(const fs::path& file, string_t& owner, string_t& group) {
- // TODO: throw exception on error
- // TODO: get user and group in C++ way?
- struct stat info;
- stat(file.c_str(), &info);
- /**
- * The return value may point to a static area, and may be
- * overwritten by subsequent calls to getpwent(3), getpw‐
- * nam(), or getpwuid(). (Do not pass the returned pointer
- * to free(3).)
- */
- struct passwd* pw = getpwuid(info.st_uid);
- struct group* gr = getgrgid(info.st_gid);
- owner = convertor.from_bytes(pw->pw_name);
- group = convertor.from_bytes(gr->gr_name);
- }
-
- string_t getXattr(const fs::path& file, const string_t& attributeName) {
- // TODO: review, test on multiple platforms
- // TODO: improve documentation in xattr.h in glibc
- // TODO: check XATTR_NAME_MAX in limits.h and if it is reasonably small on our system, allocate buffer for it instead of reading twice
- // TODO: avoid race condition (another process might change the attribute between reading its length and its value)
- ssize_t length = getxattr(file.c_str(), convertor.to_bytes(attributeName).c_str(), nullptr, 0);
- if (length > 0) {
- std::vector<char> buffer(length + 1);
- getxattr(file.c_str(), convertor.to_bytes(attributeName).c_str(), buffer.data(), buffer.size());
- return convertor.from_bytes(buffer.data());
- } else {
- return L"";
- }
- }
-
public:
- void process(std::istream& input, std::ostream& output) {
+ void process(std::istream& input, std::ostream& output, Configuration& configuration) {
std::shared_ptr<RelationalWriter> writer(Factory::create(output));
- // TODO: redesign: parametrized from CLI + groups of information fetched together and used for multiple attributes
- bool enableName = true;
- bool enableExists = false;
- bool enableNameAbsolute = false;
- bool enableNameCanonical = false;
- bool enableType = true;
- bool enableSize = true;
- bool enableOwner = true;
- bool enableGroup = true;
- bool enableXattrUrl = true; // just a demo
-
std::vector<AttributeMetadata> attributesMetadata;
- if (enableName) attributesMetadata.push_back({L"name", TypeId::STRING});
- if (enableExists) attributesMetadata.push_back({L"exists", TypeId::BOOLEAN});
- if (enableNameAbsolute) attributesMetadata.push_back({L"name_absolute", TypeId::STRING});
- if (enableNameCanonical) attributesMetadata.push_back({L"name_canonical", TypeId::STRING});
- if (enableType) attributesMetadata.push_back({L"type", TypeId::STRING});
- if (enableSize) attributesMetadata.push_back({L"size", TypeId::INTEGER});
- if (enableOwner) attributesMetadata.push_back({L"owner", TypeId::STRING});
- if (enableGroup) attributesMetadata.push_back({L"group", TypeId::STRING});
- if (enableXattrUrl) attributesMetadata.push_back({L"xattr_url", TypeId::STRING});
+ for (RequestedField field : configuration.fields) {
+ AttributeFinder* finder = attributeFinders[field.group];
+ if (finder) for (AttributeMetadata m : finder->toMetadata(field)) attributesMetadata.push_back(m);
+ else throw RelpipeWriterException(L"Unsupported field group: " + field.group);
+ }
writer->startRelation(L"filesystem", attributesMetadata, true);
+
for (std::stringstream originalName; readNext(input, originalName); reset(originalName)) {
- if (enableName) writer->writeAttribute(convertor.from_bytes(originalName.str()));
+
fs::path file(originalName.str());
bool exists = false;
@@ -140,46 +96,20 @@
// we probably do not have permissions to given directory → pretend that the file does not exist
}
- if (exists) {
- if (enableExists) writer->writeAttribute(L"true");
- if (enableNameAbsolute) writer->writeAttribute(fs::absolute(file).wstring());
- if (enableNameCanonical) writer->writeAttribute(fs::canonical(file).wstring());
- if (enableType) writer->writeAttribute(getType(file));
- if (enableSize) {
- integer_t size = fs::is_regular_file(file) ? fs::file_size(file) : 0;
- writer->writeAttribute(&size, typeid (size));
- }
+ for (auto& finder : attributeFinders) finder.second->startFile(file);
- if (enableOwner || enableGroup) {
- string_t owner;
- string_t group;
- fetchOwner(file, owner, group);
+ for (RequestedField field : configuration.fields) {
+ AttributeFinder* finder = attributeFinders[field.group]; // should not be nullptr, because already checked while writing the relation metadata
- if (enableOwner) writer->writeAttribute(owner);
- if (enableGroup) writer->writeAttribute(group);
- }
-
- if (enableXattrUrl) writer->writeAttribute(getXattr(file, L"user.xdg.origin.url"));
-
- } else {
- // Only original name was written → write remaining attributes
- // TODO: null values (requires adding null support to the format specification)
- for (int i = 1; i < attributesMetadata.size(); i++) {
- switch (attributesMetadata[i].typeId) {
- case TypeId::BOOLEAN: writer->writeAttribute(L"false");
- break;
- case TypeId::INTEGER: writer->writeAttribute(L"0");
- break;
- case TypeId::STRING: writer->writeAttribute(L"");
- break;
- default:
- throw RelpipeWriterException(L"Unsupported data type.");
-
- }
+ if (exists || (field.group == RequestedField::GROUP_FILE && field.name == FileAttributeFinder::FIELD_PATH_ORIGINAL)) {
+ finder->writeField(writer.get(), field);
+ } else {
+ finder->writeEmptyField(writer.get(), field);
}
}
+
+ for (auto& finder : attributeFinders) finder.second->endFile();
}
-
}
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/RequestedField.h Wed Jan 16 17:23:05 2019 +0100
@@ -0,0 +1,51 @@
+/**
+ * Relational pipes
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+
+#include <relpipe/writer/typedefs.h>
+
+namespace relpipe {
+namespace in {
+namespace filesystem {
+
+using namespace relpipe::writer;
+
+class RequestedField {
+public:
+ static const string_t GROUP_FILE;
+ static const string_t GROUP_XATTR;
+ string_t group;
+ string_t name;
+ std::vector<string_t> aliases;
+ std::map<string_t, string_t> options;
+
+ RequestedField(const string_t& group, const string_t& name, const std::vector<string_t>& aliases = {}, const std::map<string_t, string_t>& options = {}) : group(group), name(name), aliases(aliases), options(options) {
+ }
+
+ virtual ~RequestedField() {
+ }
+};
+
+const string_t RequestedField::GROUP_FILE = L"file";
+const string_t RequestedField::GROUP_XATTR = L"xattr";
+
+}
+}
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/XattrAttributeFinder.h Wed Jan 16 17:23:05 2019 +0100
@@ -0,0 +1,80 @@
+/**
+ * Relational pipes
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <vector>
+#include <filesystem>
+
+#include <relpipe/writer/typedefs.h>
+#include <relpipe/writer/AttributeMetadata.h>
+#include <relpipe/writer/RelationalWriter.h>
+
+#include "RequestedField.h"
+
+namespace relpipe {
+namespace in {
+namespace filesystem {
+
+namespace fs = std::filesystem;
+using namespace relpipe::writer;
+
+class XattrAttributeFinder : public AttributeFinder {
+private:
+ std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
+
+ fs::path currentFile;
+
+ string_t getXattr(const fs::path& file, const string_t& attributeName) {
+ // TODO: review, test on multiple platforms
+ // TODO: improve documentation in xattr.h in glibc
+ // TODO: check XATTR_NAME_MAX in limits.h and if it is reasonably small on our system, allocate buffer for it instead of reading twice
+ // TODO: avoid race condition (another process might change the attribute between reading its length and its value)
+ ssize_t length = getxattr(file.c_str(), convertor.to_bytes(attributeName).c_str(), nullptr, 0);
+ if (length > 0) {
+ std::vector<char> buffer(length + 1);
+ getxattr(file.c_str(), convertor.to_bytes(attributeName).c_str(), buffer.data(), buffer.size());
+ return convertor.from_bytes(buffer.data());
+ } else {
+ return L"";
+ }
+ }
+public:
+
+ virtual vector<AttributeMetadata> toMetadata(const RequestedField& field) override {
+ if (field.group == RequestedField::GROUP_XATTR) return { AttributeMetadata{field.name, TypeId::STRING}};
+ else return {};
+ }
+
+ void startFile(const fs::path& file) override {
+ currentFile = file;
+ };
+
+ void endFile() override {
+ };
+
+ virtual void writeField(RelationalWriter* writer, const RequestedField& field) override {
+ if (field.group == RequestedField::GROUP_XATTR) writer->writeAttribute(getXattr(currentFile, field.name));
+ }
+
+ virtual ~XattrAttributeFinder() override {
+ }
+};
+
+}
+}
+}
\ No newline at end of file
--- a/src/relpipe-in-filesystem.cpp Sun Jan 13 16:44:11 2019 +0100
+++ b/src/relpipe-in-filesystem.cpp Wed Jan 16 17:23:05 2019 +0100
@@ -1,6 +1,6 @@
/**
* Relational pipes
- * Copyright © 2018 František Kučera (Frantovo.cz, GlobalCode.info)
+ * Copyright © 2019 František Kučera (Frantovo.cz, GlobalCode.info)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,6 +30,7 @@
#include <relpipe/cli/CLI.h>
#include "FilesystemCommand.h"
+#include "CLIParser.h"
using namespace relpipe::cli;
using namespace relpipe::writer;
@@ -43,8 +44,10 @@
int resultCode = CLI::EXIT_CODE_UNEXPECTED_ERROR;
try {
+ CLIParser cliParser;
+ Configuration configuration = cliParser.parse(cli.arguments());
FilesystemCommand command;
- command.process(cin, cout);
+ command.process(cin, cout, configuration);
resultCode = CLI::EXIT_CODE_SUCCESS;
} catch (RelpipeWriterException e) {
fwprintf(stderr, L"Caught Writer exception: %ls\n", e.getMessge().c_str());