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 } |