1 | /* |
2 | This file is part of the KDE project |
3 | SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org> |
4 | SPDX-FileCopyrightText: 2007 Norbert Frese <nf2@scheinwelt.at> |
5 | SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org> |
6 | SPDX-FileCopyrightText: 2013-2014 Frank Reininghaus <frank78ac@googlemail.com> |
7 | |
8 | SPDX-License-Identifier: LGPL-2.0-or-later |
9 | */ |
10 | |
11 | #include "udsentry.h" |
12 | |
13 | #include "../utils_p.h" |
14 | |
15 | #include <QDataStream> |
16 | #include <QDebug> |
17 | #include <QList> |
18 | #include <QString> |
19 | |
20 | #include <KUser> |
21 | |
22 | using namespace KIO; |
23 | |
24 | // BEGIN UDSEntryPrivate |
25 | /* ---------- UDSEntryPrivate ------------ */ |
26 | |
27 | class KIO::UDSEntryPrivate : public QSharedData |
28 | { |
29 | public: |
30 | void reserve(int size); |
31 | void insert(uint udsField, const QString &value); |
32 | void replace(uint udsField, const QString &value); |
33 | void insert(uint udsField, long long value); |
34 | void replace(uint udsField, long long value); |
35 | int count() const; |
36 | QString stringValue(uint udsField) const; |
37 | long long numberValue(uint udsField, long long defaultValue = -1) const; |
38 | QList<uint> fields() const; |
39 | bool contains(uint udsField) const; |
40 | void clear(); |
41 | void save(QDataStream &s) const; |
42 | void load(QDataStream &s); |
43 | void debugUDSEntry(QDebug &stream) const; |
44 | /** |
45 | * @param field numeric UDS field id |
46 | * @return the name of the field |
47 | */ |
48 | static QString nameOfUdsField(uint field); |
49 | |
50 | private: |
51 | struct Field { |
52 | inline Field() |
53 | { |
54 | } |
55 | inline Field(const uint index, const QString &value) |
56 | : m_str(value) |
57 | , m_index(index) |
58 | { |
59 | } |
60 | inline Field(const uint index, long long value = 0) |
61 | : m_long(value) |
62 | , m_index(index) |
63 | { |
64 | } |
65 | |
66 | QString m_str; |
67 | long long m_long = LLONG_MIN; |
68 | uint m_index = 0; |
69 | }; |
70 | std::vector<Field> storage; |
71 | }; |
72 | |
73 | void UDSEntryPrivate::reserve(int size) |
74 | { |
75 | storage.reserve(n: size); |
76 | } |
77 | |
78 | void UDSEntryPrivate::insert(uint udsField, const QString &value) |
79 | { |
80 | Q_ASSERT(udsField & KIO::UDSEntry::UDS_STRING); |
81 | Q_ASSERT(std::find_if(storage.cbegin(), |
82 | storage.cend(), |
83 | [udsField](const Field &entry) { |
84 | return entry.m_index == udsField; |
85 | }) |
86 | == storage.cend()); |
87 | storage.emplace_back(args&: udsField, args: value); |
88 | } |
89 | |
90 | void UDSEntryPrivate::replace(uint udsField, const QString &value) |
91 | { |
92 | Q_ASSERT(udsField & KIO::UDSEntry::UDS_STRING); |
93 | auto it = std::find_if(first: storage.begin(), last: storage.end(), pred: [udsField](const Field &entry) { |
94 | return entry.m_index == udsField; |
95 | }); |
96 | if (it != storage.end()) { |
97 | it->m_str = value; |
98 | return; |
99 | } |
100 | storage.emplace_back(args&: udsField, args: value); |
101 | } |
102 | |
103 | void UDSEntryPrivate::insert(uint udsField, long long value) |
104 | { |
105 | Q_ASSERT(udsField & KIO::UDSEntry::UDS_NUMBER); |
106 | Q_ASSERT(std::find_if(storage.cbegin(), |
107 | storage.cend(), |
108 | [udsField](const Field &entry) { |
109 | return entry.m_index == udsField; |
110 | }) |
111 | == storage.cend()); |
112 | storage.emplace_back(args&: udsField, args&: value); |
113 | } |
114 | |
115 | void UDSEntryPrivate::replace(uint udsField, long long value) |
116 | { |
117 | Q_ASSERT(udsField & KIO::UDSEntry::UDS_NUMBER); |
118 | auto it = std::find_if(first: storage.begin(), last: storage.end(), pred: [udsField](const Field &entry) { |
119 | return entry.m_index == udsField; |
120 | }); |
121 | if (it != storage.end()) { |
122 | it->m_long = value; |
123 | return; |
124 | } |
125 | storage.emplace_back(args&: udsField, args&: value); |
126 | } |
127 | |
128 | int UDSEntryPrivate::count() const |
129 | { |
130 | return storage.size(); |
131 | } |
132 | |
133 | QString UDSEntryPrivate::stringValue(uint udsField) const |
134 | { |
135 | auto it = std::find_if(first: storage.cbegin(), last: storage.cend(), pred: [udsField](const Field &entry) { |
136 | return entry.m_index == udsField; |
137 | }); |
138 | if (it != storage.cend()) { |
139 | return it->m_str; |
140 | } |
141 | return QString(); |
142 | } |
143 | |
144 | long long UDSEntryPrivate::numberValue(uint udsField, long long defaultValue) const |
145 | { |
146 | auto it = std::find_if(first: storage.cbegin(), last: storage.cend(), pred: [udsField](const Field &entry) { |
147 | return entry.m_index == udsField; |
148 | }); |
149 | if (it != storage.cend()) { |
150 | return it->m_long; |
151 | } |
152 | return defaultValue; |
153 | } |
154 | |
155 | QList<uint> UDSEntryPrivate::fields() const |
156 | { |
157 | QList<uint> res; |
158 | res.reserve(asize: storage.size()); |
159 | for (const Field &field : storage) { |
160 | res.append(t: field.m_index); |
161 | } |
162 | return res; |
163 | } |
164 | |
165 | bool UDSEntryPrivate::contains(uint udsField) const |
166 | { |
167 | auto it = std::find_if(first: storage.cbegin(), last: storage.cend(), pred: [udsField](const Field &entry) { |
168 | return entry.m_index == udsField; |
169 | }); |
170 | return (it != storage.cend()); |
171 | } |
172 | |
173 | void UDSEntryPrivate::clear() |
174 | { |
175 | storage.clear(); |
176 | } |
177 | |
178 | void UDSEntryPrivate::save(QDataStream &s) const |
179 | { |
180 | s << static_cast<quint32>(storage.size()); |
181 | |
182 | for (const Field &field : storage) { |
183 | uint uds = field.m_index; |
184 | s << uds; |
185 | |
186 | if (uds & KIO::UDSEntry::UDS_STRING) { |
187 | s << field.m_str; |
188 | } else if (uds & KIO::UDSEntry::UDS_NUMBER) { |
189 | s << field.m_long; |
190 | } else { |
191 | Q_ASSERT_X(false, "KIO::UDSEntry" , "Found a field with an invalid type" ); |
192 | } |
193 | } |
194 | } |
195 | |
196 | void UDSEntryPrivate::load(QDataStream &s) |
197 | { |
198 | clear(); |
199 | |
200 | quint32 size; |
201 | s >> size; |
202 | reserve(size); |
203 | |
204 | // We cache the loaded strings. Some of them, like, e.g., the user, |
205 | // will often be the same for many entries in a row. Caching them |
206 | // permits to use implicit sharing to save memory. |
207 | thread_local QList<QString> cachedStrings; |
208 | if (quint32(cachedStrings.size()) < size) { |
209 | cachedStrings.resize(size); |
210 | } |
211 | |
212 | for (quint32 i = 0; i < size; ++i) { |
213 | quint32 uds; |
214 | s >> uds; |
215 | |
216 | if (uds & KIO::UDSEntry::UDS_STRING) { |
217 | // If the QString is the same like the one we read for the |
218 | // previous UDSEntry at the i-th position, use an implicitly |
219 | // shared copy of the same QString to save memory. |
220 | QString buffer; |
221 | s >> buffer; |
222 | |
223 | if (buffer != cachedStrings.at(i)) { |
224 | cachedStrings[i] = buffer; |
225 | } |
226 | |
227 | insert(udsField: uds, value: cachedStrings.at(i)); |
228 | } else if (uds & KIO::UDSEntry::UDS_NUMBER) { |
229 | long long value; |
230 | s >> value; |
231 | insert(udsField: uds, value); |
232 | } else { |
233 | Q_ASSERT_X(false, "KIO::UDSEntry" , "Found a field with an invalid type" ); |
234 | } |
235 | } |
236 | } |
237 | |
238 | QString UDSEntryPrivate::nameOfUdsField(uint field) |
239 | { |
240 | switch (field) { |
241 | case UDSEntry::UDS_SIZE: |
242 | return QStringLiteral("UDS_SIZE" ); |
243 | case UDSEntry::UDS_SIZE_LARGE: |
244 | return QStringLiteral("UDS_SIZE_LARGE" ); |
245 | case UDSEntry::UDS_USER: |
246 | return QStringLiteral("UDS_USER" ); |
247 | case UDSEntry::UDS_ICON_NAME: |
248 | return QStringLiteral("UDS_ICON_NAME" ); |
249 | case UDSEntry::UDS_GROUP: |
250 | return QStringLiteral("UDS_GROUP" ); |
251 | case UDSEntry::UDS_NAME: |
252 | return QStringLiteral("UDS_NAME" ); |
253 | case UDSEntry::UDS_LOCAL_GROUP_ID: |
254 | return QStringLiteral("UDS_LOCAL_GROUP_ID" ); |
255 | case UDSEntry::UDS_LOCAL_USER_ID: |
256 | return QStringLiteral("UDS_LOCAL_USER_ID" ); |
257 | case UDSEntry::UDS_LOCAL_PATH: |
258 | return QStringLiteral("UDS_LOCAL_PATH" ); |
259 | case UDSEntry::UDS_HIDDEN: |
260 | return QStringLiteral("UDS_HIDDEN" ); |
261 | case UDSEntry::UDS_ACCESS: |
262 | return QStringLiteral("UDS_ACCESS" ); |
263 | case UDSEntry::UDS_MODIFICATION_TIME: |
264 | return QStringLiteral("UDS_MODIFICATION_TIME" ); |
265 | case UDSEntry::UDS_ACCESS_TIME: |
266 | return QStringLiteral("UDS_ACCESS_TIME" ); |
267 | case UDSEntry::UDS_CREATION_TIME: |
268 | return QStringLiteral("UDS_CREATION_TIME" ); |
269 | case UDSEntry::UDS_FILE_TYPE: |
270 | return QStringLiteral("UDS_FILE_TYPE" ); |
271 | case UDSEntry::UDS_LINK_DEST: |
272 | return QStringLiteral("UDS_LINK_DEST" ); |
273 | case UDSEntry::UDS_URL: |
274 | return QStringLiteral("UDS_URL" ); |
275 | case UDSEntry::UDS_MIME_TYPE: |
276 | return QStringLiteral("UDS_MIME_TYPE" ); |
277 | case UDSEntry::UDS_GUESSED_MIME_TYPE: |
278 | return QStringLiteral("UDS_GUESSED_MIME_TYPE" ); |
279 | case UDSEntry::UDS_XML_PROPERTIES: |
280 | return QStringLiteral("UDS_XML_PROPERTIES" ); |
281 | case UDSEntry::UDS_EXTENDED_ACL: |
282 | return QStringLiteral("UDS_EXTENDED_ACL" ); |
283 | case UDSEntry::UDS_ACL_STRING: |
284 | return QStringLiteral("UDS_ACL_STRING" ); |
285 | case UDSEntry::UDS_DEFAULT_ACL_STRING: |
286 | return QStringLiteral("UDS_DEFAULT_ACL_STRING" ); |
287 | case UDSEntry::UDS_DISPLAY_NAME: |
288 | return QStringLiteral("UDS_DISPLAY_NAME" ); |
289 | case UDSEntry::UDS_TARGET_URL: |
290 | return QStringLiteral("UDS_TARGET_URL" ); |
291 | case UDSEntry::UDS_DISPLAY_TYPE: |
292 | return QStringLiteral("UDS_DISPLAY_TYPE" ); |
293 | case UDSEntry::UDS_ICON_OVERLAY_NAMES: |
294 | return QStringLiteral("UDS_ICON_OVERLAY_NAMES" ); |
295 | case UDSEntry::UDS_COMMENT: |
296 | return QStringLiteral("UDS_COMMENT" ); |
297 | case UDSEntry::UDS_DEVICE_ID: |
298 | return QStringLiteral("UDS_DEVICE_ID" ); |
299 | case UDSEntry::UDS_INODE: |
300 | return QStringLiteral("UDS_INODE" ); |
301 | case UDSEntry::UDS_EXTRA: |
302 | return QStringLiteral("UDS_EXTRA" ); |
303 | case UDSEntry::UDS_EXTRA_END: |
304 | return QStringLiteral("UDS_EXTRA_END" ); |
305 | default: |
306 | return QStringLiteral("Unknown uds field %1" ).arg(a: field); |
307 | } |
308 | } |
309 | |
310 | void UDSEntryPrivate::debugUDSEntry(QDebug &stream) const |
311 | { |
312 | QDebugStateSaver saver(stream); |
313 | stream.nospace() << "[" ; |
314 | for (const Field &field : storage) { |
315 | stream << " " << nameOfUdsField(field: field.m_index) << "=" ; |
316 | if (field.m_index & KIO::UDSEntry::UDS_STRING) { |
317 | stream << field.m_str; |
318 | } else if (field.m_index & KIO::UDSEntry::UDS_NUMBER) { |
319 | stream << field.m_long; |
320 | } else { |
321 | Q_ASSERT_X(false, "KIO::UDSEntry" , "Found a field with an invalid type" ); |
322 | } |
323 | } |
324 | stream << " ]" ; |
325 | } |
326 | // END UDSEntryPrivate |
327 | |
328 | // BEGIN UDSEntry |
329 | /* ---------- UDSEntry ------------ */ |
330 | |
331 | UDSEntry::UDSEntry() |
332 | : d(new UDSEntryPrivate()) |
333 | { |
334 | } |
335 | |
336 | // BUG: this API doesn't allow to handle symlinks correctly (we need buff from QT_LSTAT for most things, but buff from QT_STAT for st_mode and st_size) |
337 | UDSEntry::UDSEntry(const QT_STATBUF &buff, const QString &name) |
338 | : d(new UDSEntryPrivate()) |
339 | { |
340 | #ifndef Q_OS_WIN |
341 | d->reserve(size: 10); |
342 | #else |
343 | d->reserve(8); |
344 | #endif |
345 | d->insert(udsField: UDS_NAME, value: name); |
346 | d->insert(udsField: UDS_SIZE, value: buff.st_size); |
347 | d->insert(udsField: UDS_DEVICE_ID, value: buff.st_dev); |
348 | d->insert(udsField: UDS_INODE, value: buff.st_ino); |
349 | d->insert(udsField: UDS_FILE_TYPE, value: buff.st_mode & QT_STAT_MASK); // extract file type |
350 | d->insert(udsField: UDS_ACCESS, value: buff.st_mode & 07777); // extract permissions |
351 | d->insert(udsField: UDS_MODIFICATION_TIME, value: buff.st_mtime); |
352 | d->insert(udsField: UDS_ACCESS_TIME, value: buff.st_atime); |
353 | #ifndef Q_OS_WIN |
354 | d->insert(udsField: UDS_LOCAL_USER_ID, value: buff.st_uid); |
355 | d->insert(udsField: UDS_LOCAL_GROUP_ID, value: buff.st_gid); |
356 | #endif |
357 | } |
358 | |
359 | UDSEntry::UDSEntry(const UDSEntry &) = default; |
360 | UDSEntry::~UDSEntry() = default; |
361 | UDSEntry::UDSEntry(UDSEntry &&) = default; |
362 | UDSEntry &UDSEntry::operator=(const UDSEntry &) = default; |
363 | UDSEntry &UDSEntry::operator=(UDSEntry &&) = default; |
364 | |
365 | QString UDSEntry::stringValue(uint field) const |
366 | { |
367 | return d->stringValue(udsField: field); |
368 | } |
369 | |
370 | long long UDSEntry::numberValue(uint field, long long defaultValue) const |
371 | { |
372 | return d->numberValue(udsField: field, defaultValue); |
373 | } |
374 | |
375 | bool UDSEntry::isDir() const |
376 | { |
377 | return Utils::isDirMask(mode: numberValue(field: UDS_FILE_TYPE)); |
378 | } |
379 | |
380 | bool UDSEntry::isLink() const |
381 | { |
382 | return !stringValue(field: UDS_LINK_DEST).isEmpty(); |
383 | } |
384 | |
385 | void UDSEntry::reserve(int size) |
386 | { |
387 | d->reserve(size); |
388 | } |
389 | |
390 | void UDSEntry::fastInsert(uint field, const QString &value) |
391 | { |
392 | d->insert(udsField: field, value); |
393 | } |
394 | |
395 | void UDSEntry::fastInsert(uint field, long long value) |
396 | { |
397 | d->insert(udsField: field, value); |
398 | } |
399 | |
400 | void UDSEntry::replace(uint field, const QString &value) |
401 | { |
402 | d->replace(udsField: field, value); |
403 | } |
404 | |
405 | void UDSEntry::replace(uint field, long long value) |
406 | { |
407 | d->replace(udsField: field, value); |
408 | } |
409 | |
410 | QList<uint> UDSEntry::fields() const |
411 | { |
412 | return d->fields(); |
413 | } |
414 | |
415 | int UDSEntry::count() const |
416 | { |
417 | return d->count(); |
418 | } |
419 | |
420 | bool UDSEntry::contains(uint field) const |
421 | { |
422 | return d->contains(udsField: field); |
423 | } |
424 | |
425 | void UDSEntry::clear() |
426 | { |
427 | d->clear(); |
428 | } |
429 | // END UDSEntry |
430 | |
431 | KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KIO::UDSEntry &entry) |
432 | { |
433 | entry.d->debugUDSEntry(stream); |
434 | return stream; |
435 | } |
436 | |
437 | KIOCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KIO::UDSEntry &a) |
438 | { |
439 | a.d->save(s); |
440 | return s; |
441 | } |
442 | |
443 | KIOCORE_EXPORT QDataStream &operator>>(QDataStream &s, KIO::UDSEntry &a) |
444 | { |
445 | a.d->load(s); |
446 | return s; |
447 | } |
448 | |
449 | KIOCORE_EXPORT bool operator==(const KIO::UDSEntry &entry, const KIO::UDSEntry &other) |
450 | { |
451 | if (entry.count() != other.count()) { |
452 | return false; |
453 | } |
454 | |
455 | const QList<uint> fields = entry.fields(); |
456 | for (uint field : fields) { |
457 | if (!other.contains(field)) { |
458 | return false; |
459 | } |
460 | |
461 | if (field & UDSEntry::UDS_STRING) { |
462 | if (entry.stringValue(field) != other.stringValue(field)) { |
463 | return false; |
464 | } |
465 | } else { |
466 | if (entry.numberValue(field) != other.numberValue(field)) { |
467 | return false; |
468 | } |
469 | } |
470 | } |
471 | |
472 | return true; |
473 | } |
474 | |
475 | KIOCORE_EXPORT bool operator!=(const KIO::UDSEntry &entry, const KIO::UDSEntry &other) |
476 | { |
477 | return !(entry == other); |
478 | } |
479 | |