File indexing completed on 2024-04-21 03:55:14

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 }