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
22using namespace KIO;
23
24// BEGIN UDSEntryPrivate
25/* ---------- UDSEntryPrivate ------------ */
26
27class KIO::UDSEntryPrivate : public QSharedData
28{
29public:
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
50private:
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
73void UDSEntryPrivate::reserve(int size)
74{
75 storage.reserve(n: size);
76}
77
78void 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
90void 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
103void 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
115void 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
128int UDSEntryPrivate::count() const
129{
130 return storage.size();
131}
132
133QString 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
144long 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
155QList<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
165bool 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
173void UDSEntryPrivate::clear()
174{
175 storage.clear();
176}
177
178void 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
196void 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
238QString 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
310void 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
331UDSEntry::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)
337UDSEntry::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
359UDSEntry::UDSEntry(const UDSEntry &) = default;
360UDSEntry::~UDSEntry() = default;
361UDSEntry::UDSEntry(UDSEntry &&) = default;
362UDSEntry &UDSEntry::operator=(const UDSEntry &) = default;
363UDSEntry &UDSEntry::operator=(UDSEntry &&) = default;
364
365QString UDSEntry::stringValue(uint field) const
366{
367 return d->stringValue(udsField: field);
368}
369
370long long UDSEntry::numberValue(uint field, long long defaultValue) const
371{
372 return d->numberValue(udsField: field, defaultValue);
373}
374
375bool UDSEntry::isDir() const
376{
377 return Utils::isDirMask(mode: numberValue(field: UDS_FILE_TYPE));
378}
379
380bool UDSEntry::isLink() const
381{
382 return !stringValue(field: UDS_LINK_DEST).isEmpty();
383}
384
385void UDSEntry::reserve(int size)
386{
387 d->reserve(size);
388}
389
390void UDSEntry::fastInsert(uint field, const QString &value)
391{
392 d->insert(udsField: field, value);
393}
394
395void UDSEntry::fastInsert(uint field, long long value)
396{
397 d->insert(udsField: field, value);
398}
399
400void UDSEntry::replace(uint field, const QString &value)
401{
402 d->replace(udsField: field, value);
403}
404
405void UDSEntry::replace(uint field, long long value)
406{
407 d->replace(udsField: field, value);
408}
409
410QList<uint> UDSEntry::fields() const
411{
412 return d->fields();
413}
414
415int UDSEntry::count() const
416{
417 return d->count();
418}
419
420bool UDSEntry::contains(uint field) const
421{
422 return d->contains(udsField: field);
423}
424
425void UDSEntry::clear()
426{
427 d->clear();
428}
429// END UDSEntry
430
431KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KIO::UDSEntry &entry)
432{
433 entry.d->debugUDSEntry(stream);
434 return stream;
435}
436
437KIOCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KIO::UDSEntry &a)
438{
439 a.d->save(s);
440 return s;
441}
442
443KIOCORE_EXPORT QDataStream &operator>>(QDataStream &s, KIO::UDSEntry &a)
444{
445 a.d->load(s);
446 return s;
447}
448
449KIOCORE_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
475KIOCORE_EXPORT bool operator!=(const KIO::UDSEntry &entry, const KIO::UDSEntry &other)
476{
477 return !(entry == other);
478}
479

source code of kio/src/core/udsentry.cpp