Revision control

Copy as Markdown

// Copyright (C) 2024 Ahmad Samir <a.samirh78@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#ifndef QDIRENTRYINFO_P_H
#define QDIRENTRYINFO_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qassert.h>
#include <QtCore/private/qfileinfo_p.h>
#include <QtCore/private/qfilesystementry_p.h>
#include <QtCore/private/qfilesystemmetadata_p.h>
#include <type_traits>
#include <QtCore/q23utility.h>
#include <variant>
QT_BEGIN_NAMESPACE
namespace QDirEntryInfoPrivate {
template<class... Ts>
struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;
template <typename T, typename Variant, typename...Args>
T &emplace(Variant &v, Args&&...args)
{
#if !defined(Q_OS_VXWORKS) /* lacks LWG 2857 resolution */ \
&& !defined(Q_CC_COVERITY) /* would report "structurally dead code" */ \
/* end */
return
#endif
v.template emplace<T>(std::forward<Args>(args)...);
return *std::get_if<T>(&v);
}
//
// Reimplementation of std::visit() to fight Coverity's UNCAUGHT_EXCEPTION
//
// This version never throws std::bad_variant_access (unless thrown by the
// visitor), but Q_UNREACHABLE()s the valueless_by_exception() case. It should
// otherwise be identical to std::visit(), incl. the O(1) access.
//
// Known limitations:
// - at most three alternatives are supported (but more can be trivially added)
// - only one variant is supported (unary visitor; binary visitation isn't)
// - only works for "raw" std::variant, not "anything inheriting std::variant"
//
template <typename Visitor, typename Variant>
constexpr decltype(auto) visit(Visitor &&vis, Variant &&var)
{
using V = std::remove_reference_t<Variant>;
constexpr auto NumAlternatives = std::variant_size_v<V>;
switch (var.index()) {
#define QT_VARIANT_VISIT_CASE(num) \
case num: \
if constexpr (NumAlternatives > num) { \
const auto p = std::get_if<num>(&var); \
return std::forward<Visitor>(vis)(q23::forward_like<Variant>(*p)); \
} \
break \
/* end */
QT_VARIANT_VISIT_CASE(0);
QT_VARIANT_VISIT_CASE(1);
QT_VARIANT_VISIT_CASE(2);
static_assert(NumAlternatives <= 3, "### extend this switch if you need more");
#undef QT_VARIANT_VISIT_CASE
}
Q_UNREACHABLE();
}
} // namespace QDirEntryInfoPrivate
class QDirEntryInfo
{
template<typename QueryNative, typename QueryFileInfo, typename QueryIterator>
decltype(auto) visit(QueryNative qn, QueryFileInfo qf, QueryIterator qi)
{
auto visitor = QDirEntryInfoPrivate::overloaded{std::move(qn), std::move(qf), std::move(qi)};
return QDirEntryInfoPrivate::visit(std::move(visitor), content);
}
template<typename QueryNative, typename QueryFileInfo, typename QueryIterator>
auto query(
QueryNative &&queryNative, QueryFileInfo &&queryFileInfo, QueryIterator &&queryIterator)
{
return visit(
std::forward<QueryNative>(queryNative),
std::forward<QueryFileInfo>(queryFileInfo),
[&](const Iterator &iterator) { return queryIterator(iterator.iterator); }
);
}
template<typename QueryNative, typename QueryFileInfo>
auto query(QueryNative &&queryNative, const QueryFileInfo &queryFileInfo)
{
return visit(
std::forward<QueryNative>(queryNative),
[&](Iterator &iterator) { return queryFileInfo(iterator.ensureFileInfo()); },
queryFileInfo
);
}
public:
QDirEntryInfo() = default;
explicit QDirEntryInfo(const QAbstractFileEngineIterator *iterator)
: content(Iterator {iterator, {}})
{
}
explicit QDirEntryInfo(QFileSystemEntry &&e, QFileSystemMetaData &&md)
: content(Native { std::move(e), std::move(md) })
{
}
explicit QDirEntryInfo(QFileInfo &&info)
: content(std::move(info))
{
}
const QFileInfo &fileInfo()
{
return visit(
[](Iterator &iterator) -> const QFileInfo & { return iterator.ensureFileInfo(); },
[this](const Native &native) -> const QFileInfo & {
return QDirEntryInfoPrivate::emplace<QFileInfo>(content,
new QFileInfoPrivate(native.entry, native.metaData));
},
[](const QFileInfo &fileInfo) -> const QFileInfo & { return fileInfo; }
);
}
QString fileName()
{
return query(
[](const Native &native) { return native.entry.fileName(); },
[](const QFileInfo &info) { return info.fileName(); },
[](const QAbstractFileEngineIterator *it) { return it->currentFileName(); });
}
QString baseName()
{
return query(
[](const Native &native) { return native.entry.baseName(); },
[](const QFileInfo &info) { return info.baseName(); });
}
QString completeBaseName()
{
return query(
[](const Native &native) { return native.entry.completeBaseName(); },
[](const QFileInfo &info) { return info.completeBaseName(); });
}
QString suffix()
{
return query(
[](const Native &native) { return native.entry.suffix(); },
[](const QFileInfo &info) { return info.suffix(); });
}
QString completeSuffix()
{
return query(
[](const Native &native) { return native.entry.completeSuffix(); },
[](const QFileInfo &info) { return info.completeSuffix(); });
}
QString filePath()
{
return query(
[](const Native &native) { return native.entry.filePath(); },
[](const QFileInfo &info) { return info.filePath(); },
[](const QAbstractFileEngineIterator *it) { return it->currentFilePath(); });
}
QString bundleName() { return fileInfo().bundleName(); }
QString canonicalFilePath()
{
// QFileInfo caches these strings
return fileInfo().canonicalFilePath();
}
QString absoluteFilePath() {
// QFileInfo caches these strings
return fileInfo().absoluteFilePath();
}
QString absolutePath() {
// QFileInfo caches these strings
return fileInfo().absolutePath();
}
bool isDir() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::DirectoryType).isDirectory();
},
[](const QFileInfo &fileInfo) {return fileInfo.isDir(); });
}
bool isFile() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::FileType).isFile();
},
[](const QFileInfo &fileInfo) {return fileInfo.isFile(); });
}
bool isSymLink() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::LegacyLinkType).isLegacyLink();
},
[](const QFileInfo &fileInfo) {return fileInfo.isSymLink(); });
}
bool isSymbolicLink() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::LinkType).isLink();
},
[](const QFileInfo &fileInfo) {return fileInfo.isSymbolicLink(); });
}
bool isJunction() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::LinkType).isJunction();
},
[](const QFileInfo &fileInfo) { return fileInfo.isJunction(); });
}
QString junctionTarget() {
return query(
[](Native &native) {
return QFileSystemEngine::getJunctionTarget(native.entry, native.metaData).filePath();
},
[](const QFileInfo &fileInfo) { return fileInfo.junctionTarget(); });
}
bool exists() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::ExistsAttribute).exists();
},
[](const QFileInfo &fileInfo) {return fileInfo.exists(); });
}
bool isHidden() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::HiddenAttribute).isHidden();
},
[](const QFileInfo &fileInfo) {return fileInfo.isHidden(); });
}
bool isReadable() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::UserReadPermission).isReadable();
},
[](const QFileInfo &fileInfo) {return fileInfo.isReadable(); });
}
bool isWritable() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::UserWritePermission).isWritable();
},
[](const QFileInfo &fileInfo) {return fileInfo.isWritable(); });
}
bool isExecutable() {
return query(
[](Native &native) {
return native.ensureFilled(QFileSystemMetaData::UserExecutePermission)
.isExecutable();
},
[](const QFileInfo &fileInfo) {return fileInfo.isExecutable(); });
}
qint64 size() { return fileInfo().size(); }
QDateTime fileTime(QFile::FileTime type, const QTimeZone &tz)
{
return fileInfo().fileTime(type, tz);
}
private:
friend class QDirListingPrivate;
friend class QDirListing;
friend class QFileSystemIterator;
friend class QFSFileEngineIterator;
struct Iterator
{
// The data we can query from both the QFileInfo and the iterator is cheaper to retrieve
// from the iterator. Therefore, keep the iterator around even if we create a the QFileInfo.
const QAbstractFileEngineIterator *iterator = nullptr;
std::optional<QFileInfo> fileInfo;
const QFileInfo &ensureFileInfo()
{
if (!fileInfo.has_value())
fileInfo = iterator->currentFileInfo();
return *fileInfo;
}
};
struct Native
{
QFileSystemEntry entry;
QFileSystemMetaData metaData;
const QFileSystemMetaData &ensureFilled(QFileSystemMetaData::MetaDataFlag what)
{
if (!metaData.hasFlags(what))
QFileSystemEngine::fillMetaData(entry, metaData, what);
return metaData;
}
};
std::variant<Native, QFileInfo, Iterator> content;
};
QT_END_NAMESPACE
#endif // QDIRENTRYINFO_P_H