File indexing completed on 2024-04-28 07:43:59
0001 /* 0002 This file is part of the KDE project 0003 SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org> 0004 SPDX-FileCopyrightText: 2007 Norbert Frese <nf2@scheinwelt.at> 0005 SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org> 0006 SPDX-FileCopyrightText: 2013-2014 Frank Reininghaus <frank78ac@googlemail.com> 0007 0008 SPDX-License-Identifier: LGPL-2.0-or-later 0009 */ 0010 0011 #include "udsentry.h" 0012 0013 #include "../utils_p.h" 0014 0015 #include <QDataStream> 0016 #include <QDebug> 0017 #include <QList> 0018 #include <QString> 0019 0020 #include <KUser> 0021 0022 using namespace KIO; 0023 0024 // BEGIN UDSEntryPrivate 0025 /* ---------- UDSEntryPrivate ------------ */ 0026 0027 class KIO::UDSEntryPrivate : public QSharedData 0028 { 0029 public: 0030 void reserve(int size); 0031 void insert(uint udsField, const QString &value); 0032 void replace(uint udsField, const QString &value); 0033 void insert(uint udsField, long long value); 0034 void replace(uint udsField, long long value); 0035 int count() const; 0036 QString stringValue(uint udsField) const; 0037 long long numberValue(uint udsField, long long defaultValue = -1) const; 0038 QList<uint> fields() const; 0039 bool contains(uint udsField) const; 0040 void clear(); 0041 void save(QDataStream &s) const; 0042 void load(QDataStream &s); 0043 void debugUDSEntry(QDebug &stream) const; 0044 /** 0045 * @param field numeric UDS field id 0046 * @return the name of the field 0047 */ 0048 static QString nameOfUdsField(uint field); 0049 0050 private: 0051 struct Field { 0052 inline Field() 0053 { 0054 } 0055 inline Field(const uint index, const QString &value) 0056 : m_str(value) 0057 , m_index(index) 0058 { 0059 } 0060 inline Field(const uint index, long long value = 0) 0061 : m_long(value) 0062 , m_index(index) 0063 { 0064 } 0065 0066 QString m_str; 0067 long long m_long = LLONG_MIN; 0068 uint m_index = 0; 0069 }; 0070 std::vector<Field> storage; 0071 }; 0072 0073 void UDSEntryPrivate::reserve(int size) 0074 { 0075 storage.reserve(size); 0076 } 0077 0078 void UDSEntryPrivate::insert(uint udsField, const QString &value) 0079 { 0080 Q_ASSERT(udsField & KIO::UDSEntry::UDS_STRING); 0081 Q_ASSERT(std::find_if(storage.cbegin(), 0082 storage.cend(), 0083 [udsField](const Field &entry) { 0084 return entry.m_index == udsField; 0085 }) 0086 == storage.cend()); 0087 storage.emplace_back(udsField, value); 0088 } 0089 0090 void UDSEntryPrivate::replace(uint udsField, const QString &value) 0091 { 0092 Q_ASSERT(udsField & KIO::UDSEntry::UDS_STRING); 0093 auto it = std::find_if(storage.begin(), storage.end(), [udsField](const Field &entry) { 0094 return entry.m_index == udsField; 0095 }); 0096 if (it != storage.end()) { 0097 it->m_str = value; 0098 return; 0099 } 0100 storage.emplace_back(udsField, value); 0101 } 0102 0103 void UDSEntryPrivate::insert(uint udsField, long long value) 0104 { 0105 Q_ASSERT(udsField & KIO::UDSEntry::UDS_NUMBER); 0106 Q_ASSERT(std::find_if(storage.cbegin(), 0107 storage.cend(), 0108 [udsField](const Field &entry) { 0109 return entry.m_index == udsField; 0110 }) 0111 == storage.cend()); 0112 storage.emplace_back(udsField, value); 0113 } 0114 0115 void UDSEntryPrivate::replace(uint udsField, long long value) 0116 { 0117 Q_ASSERT(udsField & KIO::UDSEntry::UDS_NUMBER); 0118 auto it = std::find_if(storage.begin(), storage.end(), [udsField](const Field &entry) { 0119 return entry.m_index == udsField; 0120 }); 0121 if (it != storage.end()) { 0122 it->m_long = value; 0123 return; 0124 } 0125 storage.emplace_back(udsField, value); 0126 } 0127 0128 int UDSEntryPrivate::count() const 0129 { 0130 return storage.size(); 0131 } 0132 0133 QString UDSEntryPrivate::stringValue(uint udsField) const 0134 { 0135 auto it = std::find_if(storage.cbegin(), storage.cend(), [udsField](const Field &entry) { 0136 return entry.m_index == udsField; 0137 }); 0138 if (it != storage.cend()) { 0139 return it->m_str; 0140 } 0141 return QString(); 0142 } 0143 0144 long long UDSEntryPrivate::numberValue(uint udsField, long long defaultValue) const 0145 { 0146 auto it = std::find_if(storage.cbegin(), storage.cend(), [udsField](const Field &entry) { 0147 return entry.m_index == udsField; 0148 }); 0149 if (it != storage.cend()) { 0150 return it->m_long; 0151 } 0152 return defaultValue; 0153 } 0154 0155 QList<uint> UDSEntryPrivate::fields() const 0156 { 0157 QList<uint> res; 0158 res.reserve(storage.size()); 0159 for (const Field &field : storage) { 0160 res.append(field.m_index); 0161 } 0162 return res; 0163 } 0164 0165 bool UDSEntryPrivate::contains(uint udsField) const 0166 { 0167 auto it = std::find_if(storage.cbegin(), storage.cend(), [udsField](const Field &entry) { 0168 return entry.m_index == udsField; 0169 }); 0170 return (it != storage.cend()); 0171 } 0172 0173 void UDSEntryPrivate::clear() 0174 { 0175 storage.clear(); 0176 } 0177 0178 void UDSEntryPrivate::save(QDataStream &s) const 0179 { 0180 s << static_cast<quint32>(storage.size()); 0181 0182 for (const Field &field : storage) { 0183 uint uds = field.m_index; 0184 s << uds; 0185 0186 if (uds & KIO::UDSEntry::UDS_STRING) { 0187 s << field.m_str; 0188 } else if (uds & KIO::UDSEntry::UDS_NUMBER) { 0189 s << field.m_long; 0190 } else { 0191 Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); 0192 } 0193 } 0194 } 0195 0196 void UDSEntryPrivate::load(QDataStream &s) 0197 { 0198 clear(); 0199 0200 quint32 size; 0201 s >> size; 0202 reserve(size); 0203 0204 // We cache the loaded strings. Some of them, like, e.g., the user, 0205 // will often be the same for many entries in a row. Caching them 0206 // permits to use implicit sharing to save memory. 0207 thread_local QList<QString> cachedStrings; 0208 if (quint32(cachedStrings.size()) < size) { 0209 cachedStrings.resize(size); 0210 } 0211 0212 for (quint32 i = 0; i < size; ++i) { 0213 quint32 uds; 0214 s >> uds; 0215 0216 if (uds & KIO::UDSEntry::UDS_STRING) { 0217 // If the QString is the same like the one we read for the 0218 // previous UDSEntry at the i-th position, use an implicitly 0219 // shared copy of the same QString to save memory. 0220 QString buffer; 0221 s >> buffer; 0222 0223 if (buffer != cachedStrings.at(i)) { 0224 cachedStrings[i] = buffer; 0225 } 0226 0227 insert(uds, cachedStrings.at(i)); 0228 } else if (uds & KIO::UDSEntry::UDS_NUMBER) { 0229 long long value; 0230 s >> value; 0231 insert(uds, value); 0232 } else { 0233 Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); 0234 } 0235 } 0236 } 0237 0238 QString UDSEntryPrivate::nameOfUdsField(uint field) 0239 { 0240 switch (field) { 0241 case UDSEntry::UDS_SIZE: 0242 return QStringLiteral("UDS_SIZE"); 0243 case UDSEntry::UDS_SIZE_LARGE: 0244 return QStringLiteral("UDS_SIZE_LARGE"); 0245 case UDSEntry::UDS_USER: 0246 return QStringLiteral("UDS_USER"); 0247 case UDSEntry::UDS_ICON_NAME: 0248 return QStringLiteral("UDS_ICON_NAME"); 0249 case UDSEntry::UDS_GROUP: 0250 return QStringLiteral("UDS_GROUP"); 0251 case UDSEntry::UDS_NAME: 0252 return QStringLiteral("UDS_NAME"); 0253 case UDSEntry::UDS_LOCAL_GROUP_ID: 0254 return QStringLiteral("UDS_LOCAL_GROUP_ID"); 0255 case UDSEntry::UDS_LOCAL_USER_ID: 0256 return QStringLiteral("UDS_LOCAL_USER_ID"); 0257 case UDSEntry::UDS_LOCAL_PATH: 0258 return QStringLiteral("UDS_LOCAL_PATH"); 0259 case UDSEntry::UDS_HIDDEN: 0260 return QStringLiteral("UDS_HIDDEN"); 0261 case UDSEntry::UDS_ACCESS: 0262 return QStringLiteral("UDS_ACCESS"); 0263 case UDSEntry::UDS_MODIFICATION_TIME: 0264 return QStringLiteral("UDS_MODIFICATION_TIME"); 0265 case UDSEntry::UDS_ACCESS_TIME: 0266 return QStringLiteral("UDS_ACCESS_TIME"); 0267 case UDSEntry::UDS_CREATION_TIME: 0268 return QStringLiteral("UDS_CREATION_TIME"); 0269 case UDSEntry::UDS_FILE_TYPE: 0270 return QStringLiteral("UDS_FILE_TYPE"); 0271 case UDSEntry::UDS_LINK_DEST: 0272 return QStringLiteral("UDS_LINK_DEST"); 0273 case UDSEntry::UDS_URL: 0274 return QStringLiteral("UDS_URL"); 0275 case UDSEntry::UDS_MIME_TYPE: 0276 return QStringLiteral("UDS_MIME_TYPE"); 0277 case UDSEntry::UDS_GUESSED_MIME_TYPE: 0278 return QStringLiteral("UDS_GUESSED_MIME_TYPE"); 0279 case UDSEntry::UDS_XML_PROPERTIES: 0280 return QStringLiteral("UDS_XML_PROPERTIES"); 0281 case UDSEntry::UDS_EXTENDED_ACL: 0282 return QStringLiteral("UDS_EXTENDED_ACL"); 0283 case UDSEntry::UDS_ACL_STRING: 0284 return QStringLiteral("UDS_ACL_STRING"); 0285 case UDSEntry::UDS_DEFAULT_ACL_STRING: 0286 return QStringLiteral("UDS_DEFAULT_ACL_STRING"); 0287 case UDSEntry::UDS_DISPLAY_NAME: 0288 return QStringLiteral("UDS_DISPLAY_NAME"); 0289 case UDSEntry::UDS_TARGET_URL: 0290 return QStringLiteral("UDS_TARGET_URL"); 0291 case UDSEntry::UDS_DISPLAY_TYPE: 0292 return QStringLiteral("UDS_DISPLAY_TYPE"); 0293 case UDSEntry::UDS_ICON_OVERLAY_NAMES: 0294 return QStringLiteral("UDS_ICON_OVERLAY_NAMES"); 0295 case UDSEntry::UDS_COMMENT: 0296 return QStringLiteral("UDS_COMMENT"); 0297 case UDSEntry::UDS_DEVICE_ID: 0298 return QStringLiteral("UDS_DEVICE_ID"); 0299 case UDSEntry::UDS_INODE: 0300 return QStringLiteral("UDS_INODE"); 0301 case UDSEntry::UDS_EXTRA: 0302 return QStringLiteral("UDS_EXTRA"); 0303 case UDSEntry::UDS_EXTRA_END: 0304 return QStringLiteral("UDS_EXTRA_END"); 0305 default: 0306 return QStringLiteral("Unknown uds field %1").arg(field); 0307 } 0308 } 0309 0310 void UDSEntryPrivate::debugUDSEntry(QDebug &stream) const 0311 { 0312 QDebugStateSaver saver(stream); 0313 stream.nospace() << "["; 0314 for (const Field &field : storage) { 0315 stream << " " << nameOfUdsField(field.m_index) << "="; 0316 if (field.m_index & KIO::UDSEntry::UDS_STRING) { 0317 stream << field.m_str; 0318 } else if (field.m_index & KIO::UDSEntry::UDS_NUMBER) { 0319 stream << field.m_long; 0320 } else { 0321 Q_ASSERT_X(false, "KIO::UDSEntry", "Found a field with an invalid type"); 0322 } 0323 } 0324 stream << " ]"; 0325 } 0326 // END UDSEntryPrivate 0327 0328 // BEGIN UDSEntry 0329 /* ---------- UDSEntry ------------ */ 0330 0331 UDSEntry::UDSEntry() 0332 : d(new UDSEntryPrivate()) 0333 { 0334 } 0335 0336 // 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) 0337 UDSEntry::UDSEntry(const QT_STATBUF &buff, const QString &name) 0338 : d(new UDSEntryPrivate()) 0339 { 0340 #ifndef Q_OS_WIN 0341 d->reserve(10); 0342 #else 0343 d->reserve(8); 0344 #endif 0345 d->insert(UDS_NAME, name); 0346 d->insert(UDS_SIZE, buff.st_size); 0347 d->insert(UDS_DEVICE_ID, buff.st_dev); 0348 d->insert(UDS_INODE, buff.st_ino); 0349 d->insert(UDS_FILE_TYPE, buff.st_mode & QT_STAT_MASK); // extract file type 0350 d->insert(UDS_ACCESS, buff.st_mode & 07777); // extract permissions 0351 d->insert(UDS_MODIFICATION_TIME, buff.st_mtime); 0352 d->insert(UDS_ACCESS_TIME, buff.st_atime); 0353 #ifndef Q_OS_WIN 0354 d->insert(UDS_LOCAL_USER_ID, buff.st_uid); 0355 d->insert(UDS_LOCAL_GROUP_ID, buff.st_gid); 0356 #endif 0357 } 0358 0359 UDSEntry::UDSEntry(const UDSEntry &) = default; 0360 UDSEntry::~UDSEntry() = default; 0361 UDSEntry::UDSEntry(UDSEntry &&) = default; 0362 UDSEntry &UDSEntry::operator=(const UDSEntry &) = default; 0363 UDSEntry &UDSEntry::operator=(UDSEntry &&) = default; 0364 0365 QString UDSEntry::stringValue(uint field) const 0366 { 0367 return d->stringValue(field); 0368 } 0369 0370 long long UDSEntry::numberValue(uint field, long long defaultValue) const 0371 { 0372 return d->numberValue(field, defaultValue); 0373 } 0374 0375 bool UDSEntry::isDir() const 0376 { 0377 return Utils::isDirMask(numberValue(UDS_FILE_TYPE)); 0378 } 0379 0380 bool UDSEntry::isLink() const 0381 { 0382 return !stringValue(UDS_LINK_DEST).isEmpty(); 0383 } 0384 0385 void UDSEntry::reserve(int size) 0386 { 0387 d->reserve(size); 0388 } 0389 0390 void UDSEntry::fastInsert(uint field, const QString &value) 0391 { 0392 d->insert(field, value); 0393 } 0394 0395 void UDSEntry::fastInsert(uint field, long long value) 0396 { 0397 d->insert(field, value); 0398 } 0399 0400 void UDSEntry::replace(uint field, const QString &value) 0401 { 0402 d->replace(field, value); 0403 } 0404 0405 void UDSEntry::replace(uint field, long long value) 0406 { 0407 d->replace(field, value); 0408 } 0409 0410 QList<uint> UDSEntry::fields() const 0411 { 0412 return d->fields(); 0413 } 0414 0415 int UDSEntry::count() const 0416 { 0417 return d->count(); 0418 } 0419 0420 bool UDSEntry::contains(uint field) const 0421 { 0422 return d->contains(field); 0423 } 0424 0425 void UDSEntry::clear() 0426 { 0427 d->clear(); 0428 } 0429 // END UDSEntry 0430 0431 KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KIO::UDSEntry &entry) 0432 { 0433 entry.d->debugUDSEntry(stream); 0434 return stream; 0435 } 0436 0437 KIOCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KIO::UDSEntry &a) 0438 { 0439 a.d->save(s); 0440 return s; 0441 } 0442 0443 KIOCORE_EXPORT QDataStream &operator>>(QDataStream &s, KIO::UDSEntry &a) 0444 { 0445 a.d->load(s); 0446 return s; 0447 } 0448 0449 KIOCORE_EXPORT bool operator==(const KIO::UDSEntry &entry, const KIO::UDSEntry &other) 0450 { 0451 if (entry.count() != other.count()) { 0452 return false; 0453 } 0454 0455 const QList<uint> fields = entry.fields(); 0456 for (uint field : fields) { 0457 if (!other.contains(field)) { 0458 return false; 0459 } 0460 0461 if (field & UDSEntry::UDS_STRING) { 0462 if (entry.stringValue(field) != other.stringValue(field)) { 0463 return false; 0464 } 0465 } else { 0466 if (entry.numberValue(field) != other.numberValue(field)) { 0467 return false; 0468 } 0469 } 0470 } 0471 0472 return true; 0473 } 0474 0475 KIOCORE_EXPORT bool operator!=(const KIO::UDSEntry &entry, const KIO::UDSEntry &other) 0476 { 0477 return !(entry == other); 0478 }