src/FilesystemCommand.h
branchv_0
changeset 4 d44ed75822e7
parent 3 62eac7ab4cf4
child 7 8d73bff730a7
--- 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();
 		}
-
 	}
 };