src/FilesystemCommand.h
branchv_0
changeset 2 f07ed604a0ab
parent 0 467d09b62a12
child 3 62eac7ab4cf4
equal deleted inserted replaced
1:5237524cbb2b 2:f07ed604a0ab
    19 
    19 
    20 #include <cstdlib>
    20 #include <cstdlib>
    21 #include <iostream>
    21 #include <iostream>
    22 #include <string>
    22 #include <string>
    23 #include <vector>
    23 #include <vector>
       
    24 #include <array>
    24 #include <algorithm>
    25 #include <algorithm>
       
    26 #include <filesystem>
       
    27 
       
    28 #include <pwd.h>
       
    29 #include <grp.h>
       
    30 #include <sys/stat.h>
       
    31 
       
    32 #include <sys/xattr.h>
    25 
    33 
    26 #include <relpipe/writer/typedefs.h>
    34 #include <relpipe/writer/typedefs.h>
    27 
    35 
    28 namespace relpipe {
    36 namespace relpipe {
    29 namespace in {
    37 namespace in {
    30 namespace filesystem {
    38 namespace filesystem {
    31 
    39 
       
    40 namespace fs = std::filesystem;
       
    41 using namespace relpipe::writer;
       
    42 
    32 class FilesystemCommand {
    43 class FilesystemCommand {
       
    44 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encodings.
       
    45 
       
    46 	void reset(std::stringstream& stream) {
       
    47 		stream.str("");
       
    48 		stream.clear();
       
    49 	}
       
    50 
       
    51 	bool readNext(std::istream& input, std::stringstream& originalName) {
       
    52 		for (char ch; input.get(ch);) {
       
    53 			if (ch == 0) return true;
       
    54 			else originalName << ch;
       
    55 		}
       
    56 		return originalName.tellp();
       
    57 	}
       
    58 
       
    59 	string_t getType(const fs::path& file) {
       
    60 		// TODO: Use whole words? (letters are compatible with find -type)
       
    61 		if (fs::is_regular_file(file)) return L"f";
       
    62 		else if (fs::is_symlink(file)) return L"l"; // symlinks to directories are both symlinks and directories
       
    63 		else if (fs::is_directory(file)) return L"d";
       
    64 		else if (fs::is_fifo(file)) return L"p";
       
    65 		else if (fs::is_socket(file)) return L"s";
       
    66 		else if (fs::is_block_file(file)) return L"b";
       
    67 		else if (fs::is_character_file(file)) return L"c";
       
    68 		else return L"o";
       
    69 	}
       
    70 
       
    71 	void fetchOwner(const fs::path& file, string_t& owner, string_t& group) {
       
    72 		// TODO: throw exception on error
       
    73 		// TODO: get user and group in C++ way?
       
    74 		struct stat info;
       
    75 		stat(file.c_str(), &info);
       
    76 		/**
       
    77 		 * The return value may point to a static area, and may  be
       
    78 		 * overwritten  by  subsequent calls to getpwent(3), getpw‐
       
    79 		 * nam(), or getpwuid().  (Do not pass the returned pointer
       
    80 		 * to free(3).)
       
    81 		 */
       
    82 		struct passwd* pw = getpwuid(info.st_uid);
       
    83 		struct group* gr = getgrgid(info.st_gid);
       
    84 		owner = convertor.from_bytes(pw->pw_name);
       
    85 		group = convertor.from_bytes(gr->gr_name);
       
    86 	}
       
    87 
       
    88 	string_t getXattr(const fs::path& file, const string_t& attributeName) {
       
    89 		// TODO: review, test on multiple platforms
       
    90 		ssize_t length = getxattr(file.c_str(), convertor.to_bytes(attributeName).c_str(), nullptr, 0);
       
    91 		if (length > 0) {
       
    92 			std::vector<char> buffer(length + 1);
       
    93 			getxattr(file.c_str(), convertor.to_bytes(attributeName).c_str(), buffer.data(), length);
       
    94 			return convertor.from_bytes(buffer.data());
       
    95 		} else {
       
    96 			return L"";
       
    97 		}
       
    98 	}
       
    99 
    33 public:
   100 public:
    34 
   101 
    35 	void process(std::istream& input, std::ostream& output) {
   102 	void process(std::istream& input, std::ostream& output) {
    36 		using namespace relpipe::writer;
       
    37 		std::shared_ptr<RelationalWriter> writer(Factory::create(output));
   103 		std::shared_ptr<RelationalWriter> writer(Factory::create(output));
    38 
   104 
    39 		// TODO: remove demo
   105 		// TODO: redesign: parametrized from CLI + groups of information fetched together and used for multiple attributes
    40 		// Various data types passed as strings
   106 		bool enableName = true;
    41 		writer->startRelation(L"filesystem",{
   107 		bool enableExists = false;
    42 			{L"s", TypeId::STRING},
   108 		bool enableNameAbsolute = false;
    43 			{L"i", TypeId::INTEGER},
   109 		bool enableNameCanonical = false;
    44 			{L"b", TypeId::BOOLEAN}
   110 		bool enableType = true;
    45 		}, true);
   111 		bool enableSize = true;
       
   112 		bool enableOwner = true;
       
   113 		bool enableGroup = true;
       
   114 		bool enableXattrUrl = true; // just a demo
    46 
   115 
    47 		writer->writeAttribute(L"a");
   116 		std::vector<AttributeMetadata> attributesMetadata;
    48 		writer->writeAttribute(L"1");
   117 		if (enableName) attributesMetadata.push_back({L"name", TypeId::STRING});
    49 		writer->writeAttribute(L"true");
   118 		if (enableExists) attributesMetadata.push_back({L"exists", TypeId::BOOLEAN});
    50 		
   119 		if (enableNameAbsolute) attributesMetadata.push_back({L"name_absolute", TypeId::STRING});
       
   120 		if (enableNameCanonical) attributesMetadata.push_back({L"name_canonical", TypeId::STRING});
       
   121 		if (enableType) attributesMetadata.push_back({L"type", TypeId::STRING});
       
   122 		if (enableSize) attributesMetadata.push_back({L"size", TypeId::INTEGER});
       
   123 		if (enableOwner) attributesMetadata.push_back({L"owner", TypeId::STRING});
       
   124 		if (enableGroup) attributesMetadata.push_back({L"group", TypeId::STRING});
       
   125 		if (enableXattrUrl) attributesMetadata.push_back({L"xattr_url", TypeId::STRING});
       
   126 
       
   127 		writer->startRelation(L"filesystem", attributesMetadata, true);
       
   128 
       
   129 		for (std::stringstream originalName; readNext(input, originalName); reset(originalName)) {
       
   130 			if (enableName) writer->writeAttribute(convertor.from_bytes(originalName.str()));
       
   131 			fs::path file(originalName.str());
       
   132 			bool exists = false;
       
   133 
       
   134 			try {
       
   135 				exists = fs::exists(file);
       
   136 			} catch (const fs::filesystem_error& e) {
       
   137 				// we probably do not have permissions to given directory → pretend that the file does not exist
       
   138 			}
       
   139 
       
   140 			if (exists) {
       
   141 				if (enableExists) writer->writeAttribute(L"true");
       
   142 				if (enableNameAbsolute) writer->writeAttribute(fs::absolute(file).wstring());
       
   143 				if (enableNameCanonical) writer->writeAttribute(fs::canonical(file).wstring());
       
   144 				if (enableType) writer->writeAttribute(getType(file));
       
   145 				if (enableSize) {
       
   146 					integer_t size = fs::is_regular_file(file) ? fs::file_size(file) : 0;
       
   147 					writer->writeAttribute(&size, typeid (size));
       
   148 				}
       
   149 
       
   150 				if (enableOwner || enableGroup) {
       
   151 					string_t owner;
       
   152 					string_t group;
       
   153 					fetchOwner(file, owner, group);
       
   154 
       
   155 					if (enableOwner) writer->writeAttribute(owner);
       
   156 					if (enableGroup) writer->writeAttribute(group);
       
   157 				}
       
   158 
       
   159 				if (enableXattrUrl) writer->writeAttribute(getXattr(file, L"user.xdg.origin.url"));
       
   160 
       
   161 			} else {
       
   162 				// Only original name was written → write remaining attributes
       
   163 				// TODO: null values (requires adding null support to the format specification)
       
   164 				for (int i = 1; i < attributesMetadata.size(); i++) {
       
   165 					switch (attributesMetadata[i].typeId) {
       
   166 						case TypeId::BOOLEAN: writer->writeAttribute(L"false");
       
   167 							break;
       
   168 						case TypeId::INTEGER: writer->writeAttribute(L"0");
       
   169 							break;
       
   170 						case TypeId::STRING: writer->writeAttribute(L"");
       
   171 							break;
       
   172 						default:
       
   173 							throw RelpipeWriterException(L"Unsupported data type.");
       
   174 
       
   175 					}
       
   176 				}
       
   177 			}
       
   178 		}
       
   179 
    51 	}
   180 	}
    52 };
   181 };
    53 
   182 
    54 }
   183 }
    55 }
   184 }