QDirListing: add flags to handle entries filtering

By extending IteratorFlag so that it replaces both QDir::Filter and
QDirIterator::IteratorFlag enums, but with better defaults (based on how
QDir/Iterator is used in 15-20 years worth of code in Qt and KDE).

Make the QDirListing(QDir ~~) ctor private, also change it to use
QDirIterator::IteratatorFlags; it will be used to port existing code.

If QDir is ported to use QDirListing::IteratorFlags, instead of
QDir::Filters, a public QDirListing(QDir) constructor can then be added.

Fixes: QTBUG-125504
Task-number: QTBUG-125859
Change-Id: Ide4ff8279f554029ac30d0579b0e8373ed4337f7
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
(cherry picked from commit e583c3d5163a5512abac85e32359652e28a053f7)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Ahmad Samir 2024-06-21 00:59:45 +03:00 committed by Qt Cherry-pick Bot
parent 0cbf579ca1
commit 7c663d04d7
40 changed files with 632 additions and 347 deletions

View File

@ -314,9 +314,9 @@ void NmakeMakefileGenerator::writeImplicitRulesPart(QTextStream &t)
const QStringList sourceFilesFilter = sourceFilesForImplicitRulesFilter(); const QStringList sourceFilesFilter = sourceFilesForImplicitRulesFilter();
QStringList fixifiedSourceDirs = fileFixify(QList<QString>(source_directories.constBegin(), source_directories.constEnd()), FileFixifyAbsolute); QStringList fixifiedSourceDirs = fileFixify(QList<QString>(source_directories.constBegin(), source_directories.constEnd()), FileFixifyAbsolute);
fixifiedSourceDirs.removeDuplicates(); fixifiedSourceDirs.removeDuplicates();
constexpr auto filters = QDir::Files | QDir::NoDotAndDotDot; using F = QDirListing::IteratorFlag;
for (const QString &sourceDir : std::as_const(fixifiedSourceDirs)) { for (const QString &sourceDir : std::as_const(fixifiedSourceDirs)) {
for (const auto &dirEntry : QDirListing(sourceDir, sourceFilesFilter, filters)) { for (const auto &dirEntry : QDirListing(sourceDir, sourceFilesFilter, F::FilesOnly)) {
QString &duplicate = fileNames[dirEntry.completeBaseName()]; QString &duplicate = fileNames[dirEntry.completeBaseName()];
if (duplicate.isNull()) { if (duplicate.isNull()) {
duplicate = dirEntry.filePath(); duplicate = dirEntry.filePath();

View File

@ -1293,9 +1293,8 @@ void VcprojGenerator::initDeploymentTool()
} }
int pathSize = searchPath.size(); int pathSize = searchPath.size();
constexpr auto filters = QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks;
using F = QDirListing::IteratorFlag; using F = QDirListing::IteratorFlag;
QDirListing dirList(searchPath, QStringList{nameFilter}, filters, F::Recursive); QDirListing dirList(searchPath, QStringList{nameFilter}, F::FilesOnly | F::Recursive);
// foreach dirIterator-entry in d // foreach dirIterator-entry in d
for (const auto &dirEntry : dirList) { for (const auto &dirEntry : dirList) {
const QString absoluteItemPath = Option::fixPathToTargetOS(dirEntry.absolutePath()); const QString absoluteItemPath = Option::fixPathToTargetOS(dirEntry.absolutePath());

View File

@ -35,7 +35,8 @@ for (const auto &dirEntry : dirList) {
{ {
//! [2] //! [2]
QDirListing audioFileIt(u"/home/johndoe/"_s, {"*.mp3", "*.wav"}, QDir::Files); QDirListing audioFileIt(u"/home/johndoe/"_s, QStringList{u"*.mp3"_s, u"*.wav"_s},
QDirListing::IteratorFlag::FilesOnly);
//! [2] //! [2]
} }

View File

@ -590,6 +590,21 @@ bool QAbstractFileEngine::isRelativePath() const
\sa setFileName() \sa setFileName()
*/ */
QStringList QAbstractFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const QStringList QAbstractFileEngine::entryList(QDir::Filters filters, const QStringList &filterNames) const
{
QStringList ret;
#ifdef QT_BOOTSTRAPPED
Q_UNUSED(filters);
Q_UNUSED(filterNames);
Q_UNREACHABLE_RETURN(ret);
#else
for (const auto &dirEntry : QDirListing(fileName(), filterNames, filters.toInt()))
ret.emplace_back(dirEntry.fileName());
return ret;
#endif
}
QStringList QAbstractFileEngine::entryList(QDirListing::IteratorFlags filters,
const QStringList &filterNames) const
{ {
QStringList ret; QStringList ret;
#ifdef QT_BOOTSTRAPPED #ifdef QT_BOOTSTRAPPED
@ -902,6 +917,15 @@ QAbstractFileEngineIterator::QAbstractFileEngineIterator(const QString &path, QD
{ {
} }
QAbstractFileEngineIterator::QAbstractFileEngineIterator(const QString &path,
QDirListing::IteratorFlags filters,
const QStringList &nameFilters)
: m_listingFilters(filters),
m_nameFilters(nameFilters),
m_path(appendSlashIfNeeded(path))
{
}
/*! /*!
Destroys the QAbstractFileEngineIterator. Destroys the QAbstractFileEngineIterator.
@ -1017,6 +1041,16 @@ QAbstractFileEngine::beginEntryList(const QString &path, QDir::Filters filters,
return {}; return {};
} }
QAbstractFileEngine::IteratorUniquePtr
QAbstractFileEngine::beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
{
Q_UNUSED(path);
Q_UNUSED(filters);
Q_UNUSED(filterNames);
return {};
}
/*! /*!
Reads a number of characters from the file into \a data. At most Reads a number of characters from the file into \a data. At most
\a maxlen characters will be read. \a maxlen characters will be read.

View File

@ -18,6 +18,7 @@
#include <QtCore/private/qglobal_p.h> #include <QtCore/private/qglobal_p.h>
#include "QtCore/qfile.h" #include "QtCore/qfile.h"
#include "QtCore/qdir.h" #include "QtCore/qdir.h"
#include "QtCore/qdirlisting.h"
#include <memory> #include <memory>
#include <optional> #include <optional>
@ -106,6 +107,8 @@ public:
virtual bool caseSensitive() const; virtual bool caseSensitive() const;
virtual bool isRelativePath() const; virtual bool isRelativePath() const;
virtual QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const; virtual QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const;
virtual QStringList entryList(QDirListing::IteratorFlags filters,
const QStringList &filterNames) const;
virtual FileFlags fileFlags(FileFlags type=FileInfoAll) const; virtual FileFlags fileFlags(FileFlags type=FileInfoAll) const;
virtual bool setPermissions(uint perms); virtual bool setPermissions(uint perms);
virtual QByteArray id() const; virtual QByteArray id() const;
@ -128,6 +131,10 @@ public:
beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames); beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames);
virtual IteratorUniquePtr endEntryList() { return {}; } virtual IteratorUniquePtr endEntryList() { return {}; }
virtual IteratorUniquePtr
beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames);
virtual qint64 read(char *data, qint64 maxlen); virtual qint64 read(char *data, qint64 maxlen);
virtual qint64 readLine(char *data, qint64 maxlen); virtual qint64 readLine(char *data, qint64 maxlen);
virtual qint64 write(const char *data, qint64 len); virtual qint64 write(const char *data, qint64 len);
@ -203,6 +210,8 @@ class Q_CORE_EXPORT QAbstractFileEngineIterator
public: public:
QAbstractFileEngineIterator(const QString &path, QDir::Filters filters, QAbstractFileEngineIterator(const QString &path, QDir::Filters filters,
const QStringList &nameFilters); const QStringList &nameFilters);
QAbstractFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &nameFilters);
virtual ~QAbstractFileEngineIterator(); virtual ~QAbstractFileEngineIterator();
virtual bool advance() = 0; virtual bool advance() = 0;
@ -225,6 +234,7 @@ private:
friend class QDirListingPrivate; friend class QDirListingPrivate;
QDir::Filters m_filters; QDir::Filters m_filters;
QDirListing::IteratorFlags m_listingFilters;
QStringList m_nameFilters; QStringList m_nameFilters;
QString m_path; QString m_path;
}; };

View File

@ -347,8 +347,10 @@ inline void QDirPrivate::initFileLists(const QDir &dir) const
QMutexLocker locker(&fileCache.mutex); QMutexLocker locker(&fileCache.mutex);
if (!fileCache.fileListsInitialized) { if (!fileCache.fileListsInitialized) {
QFileInfoList l; QFileInfoList l;
for (const auto &dirEntry : QDirListing(dir)) for (const auto &dirEntry : QDirListing(dir.path(), dir.nameFilters(),
dir.filter().toInt())) {
l.emplace_back(dirEntry.fileInfo()); l.emplace_back(dirEntry.fileInfo());
}
sortFileList(sort, l, &fileCache.files, &fileCache.fileInfos); sortFileList(sort, l, &fileCache.files, &fileCache.fileInfos);
fileCache.fileListsInitialized = true; fileCache.fileListsInitialized = true;
@ -1427,7 +1429,7 @@ QStringList QDir::entryList(const QStringList &nameFilters, Filters filters,
} }
} }
QDirListing dirList(d->dirEntry.filePath(), nameFilters, filters); QDirListing dirList(d->dirEntry.filePath(), nameFilters, filters.toInt());
QStringList ret; QStringList ret;
if (needsSorting) { if (needsSorting) {
QFileInfoList l; QFileInfoList l;
@ -1473,7 +1475,7 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter
} }
QFileInfoList l; QFileInfoList l;
for (const auto &dirEntry : QDirListing(d->dirEntry.filePath(), nameFilters, filters)) for (const auto &dirEntry : QDirListing(d->dirEntry.filePath(), nameFilters, filters.toInt()))
l.emplace_back(dirEntry.fileInfo()); l.emplace_back(dirEntry.fileInfo());
QFileInfoList ret; QFileInfoList ret;
d->sortFileList(sort, l, nullptr, &ret); d->sortFileList(sort, l, nullptr, &ret);
@ -1645,8 +1647,7 @@ bool QDir::removeRecursively()
bool success = true; bool success = true;
const QString dirPath = path(); const QString dirPath = path();
// not empty -- we must empty it first // not empty -- we must empty it first
constexpr auto dirFilters = QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot; for (const auto &dirEntry : QDirListing(dirPath, QDirListing::IteratorFlag::IncludeHidden)) {
for (const auto &dirEntry : QDirListing(dirPath, dirFilters)) {
const QString &filePath = dirEntry.filePath(); const QString &filePath = dirEntry.filePath();
bool ok; bool ok;
if (dirEntry.isDir() && !dirEntry.isSymLink()) { if (dirEntry.isDir() && !dirEntry.isSymLink()) {
@ -1969,7 +1970,7 @@ bool QDir::exists(const QString &name) const
bool QDir::isEmpty(Filters filters) const bool QDir::isEmpty(Filters filters) const
{ {
Q_D(const QDir); Q_D(const QDir);
QDirListing dirList(d->dirEntry.filePath(), d->nameFilters, filters); QDirListing dirList(d->dirEntry.filePath(), d->nameFilters, filters.toInt());
return dirList.cbegin() == dirList.cend(); return dirList.cbegin() == dirList.cend();
} }
#endif // !QT_BOOTSTRAPPED #endif // !QT_BOOTSTRAPPED

View File

@ -6,6 +6,7 @@
#include <QtCore/qcompare.h> #include <QtCore/qcompare.h>
#include <QtCore/qstring.h> #include <QtCore/qstring.h>
#include <QtCore/qdirlisting.h>
#include <QtCore/qfile.h> #include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h> #include <QtCore/qfileinfo.h>
#include <QtCore/qstringlist.h> #include <QtCore/qstringlist.h>
@ -244,6 +245,7 @@ private:
Q_DECLARE_EQUALITY_COMPARABLE(QDir) Q_DECLARE_EQUALITY_COMPARABLE(QDir)
friend class QDirIterator; friend class QDirIterator;
friend class QDirListing; friend class QDirListing;
friend class QDirListingPrivate;
// Q_DECLARE_PRIVATE equivalent for shared data pointers // Q_DECLARE_PRIVATE equivalent for shared data pointers
QDirPrivate *d_func(); QDirPrivate *d_func();
const QDirPrivate *d_func() const { return d_ptr.constData(); } const QDirPrivate *d_func() const { return d_ptr.constData(); }

View File

@ -86,44 +86,12 @@ using namespace Qt::StringLiterals;
class QDirIteratorPrivate class QDirIteratorPrivate
{ {
static QDirListing::IteratorFlags toDirListingFlags(QDirIterator::IteratorFlags flags)
{
using F = QDirListing::IteratorFlag;
QDirListing::IteratorFlags listerFlags;
if (flags & QDirIterator::NoIteratorFlags)
listerFlags.setFlag(F::NoFlag);
if (flags & QDirIterator::FollowSymlinks)
listerFlags.setFlag(F::FollowSymlinks);
if (flags & QDirIterator::Subdirectories)
listerFlags.setFlag(F::Recursive);
return listerFlags;
}
public: public:
QDirIteratorPrivate(const QDir &dir, QDirIterator::IteratorFlags flags) QDirIteratorPrivate(const QString &path, const QStringList &nameFilters = {},
: lister(dir, toDirListingFlags(flags)) QDir::Filters filters = QDir::NoFilter,
{ QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags)
init(); : lister(path, nameFilters, filters.toInt(), flags.toInt())
} { init(); }
QDirIteratorPrivate(const QString &path, QDirIterator::IteratorFlags flags)
: lister(path, toDirListingFlags(flags))
{
init();
}
QDirIteratorPrivate(const QString &path, QDir::Filters filters,
QDirIterator::IteratorFlags flags)
: lister(path, filters, toDirListingFlags(flags))
{
init();
}
QDirIteratorPrivate(const QString &path, const QStringList &nameFilters, QDir::Filters filters,
QDirIterator::IteratorFlags flags)
: lister(path, nameFilters, filters, toDirListingFlags(flags))
{
init();
}
void init() void init()
{ {
@ -162,7 +130,7 @@ public:
\sa hasNext(), next(), IteratorFlags \sa hasNext(), next(), IteratorFlags
*/ */
QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags) QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
: d(new QDirIteratorPrivate(dir, flags)) : d(new QDirIteratorPrivate(dir.path(), dir.nameFilters(), dir.filter(), flags))
{ {
} }
@ -180,7 +148,7 @@ QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
\sa hasNext(), next(), IteratorFlags \sa hasNext(), next(), IteratorFlags
*/ */
QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags) QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags)
: d(new QDirIteratorPrivate(path, filters, flags)) : d(new QDirIteratorPrivate(path, {}, filters, flags))
{ {
} }
@ -197,7 +165,7 @@ QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorF
\sa hasNext(), next(), IteratorFlags \sa hasNext(), next(), IteratorFlags
*/ */
QDirIterator::QDirIterator(const QString &path, IteratorFlags flags) QDirIterator::QDirIterator(const QString &path, IteratorFlags flags)
: d(new QDirIteratorPrivate(path, flags)) : d(new QDirIteratorPrivate(path, {}, QDir::NoFilter, flags))
{ {
} }

View File

@ -15,7 +15,7 @@
directory contents recursively, and following symbolic links. Unlike directory contents recursively, and following symbolic links. Unlike
QDir::entryList(), QDirListing does not support sorting. QDir::entryList(), QDirListing does not support sorting.
The QDirListing constructor takes a QDir or a directory path as The QDirListing constructor takes a directory path string as
argument. Here's how to iterate over all entries recursively: argument. Here's how to iterate over all entries recursively:
\snippet code/src_corelib_io_qdirlisting.cpp 0 \snippet code/src_corelib_io_qdirlisting.cpp 0
@ -45,24 +45,73 @@
/*! \enum QDirListing::IteratorFlag /*! \enum QDirListing::IteratorFlag
This enum class describes flags can be used to configure the behavior of This enum class describes flags that can be used to configure the behavior
QDirListing. These flags can be bitwise OR'ed together. of QDirListing. Values from this enumerator can be bitwise OR'ed together.
\value NoFlag The default value, representing no flags. The iterator \value Default
will return entries for the assigned path. List all files, directories and symbolic links, including broken
symlinks (where the target doesn't exist).
Hidden files and directories and the special entries \c{.} and \c{..}
aren't listed by default.
\value FollowSymlinks When combined with Recursive, this flag enables \value ExcludeFiles
iterating through all subdirectories of the assigned path, following Don't list regular files. When combined with ResolveSymlinks, symbolic
all symbolic links. Symbolic link loops (e.g., link => . or link => links to regular files will be excluded too.
..) are automatically detected and ignored.
\value Recursive List entries inside all subdirectories as well. \value ExcludeDirs
Don't list directories. When combined with ResolveSymlinks, symbolic
links to directories will be excluded too.
\value ExcludeSpecial
Don't list special system files:
\list
\li On Unix: an entry that is not a directory, regular file or
symbolic link (including broken symlinks). That is, FIFO,
socket, character device, or block device.
\li On Windows: \c {.lnk}.
\endlist
\value ResolveSymlinks
Filter symbolic links based on the type of the target of the link,
rather than the symbolic link itself. With this flag, broken symbolic
links (where the target doesn't exist) are excluded. This flag is
ignored on operating systems that don't support symbolic links.
\value FilesOnly
Only regular files will be listed. When combined with ResolveSymlinks,
symbolic links to files will also be listed.
\value DirsOnly
Only directories will be listed. When combined with ResolveSymlinks,
symbolic links to directories will also be listed.
\value IncludeHidden
List hidden entries. When combined with Recursive, the iteration will
recurse into hidden sub-directories as well.
\value IncludeDotAndDotDot
List the \c {.} and \c{..} special entries.
\value CaseSensitive
The file glob patterns in the name filters passed to the QDirListing
constructor, will be matched case sensitively (for details, see
QDir::setNameFilters()).
\value Recursive
List entries inside all sub-directories as well. When combined with
FollowDirSymlinks, symbolic links to directories will be iterated too.
\value FollowDirSymlinks
When combined with Recursive, symbolic links to directories will be
iterated too. Symbolic link loops (e.g., link => . or link => ..) are
automatically detected and ignored.
*/ */
#include "qdirlisting.h" #include "qdirlisting.h"
#include "qdirentryinfo_p.h" #include "qdirentryinfo_p.h"
#include "qdir_p.h" #include "qdir_p.h"
#include "qdiriterator.h"
#include "qabstractfileengine_p.h" #include "qabstractfileengine_p.h"
#include <QtCore/qset.h> #include <QtCore/qset.h>
@ -85,6 +134,21 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
static QDirListing::IteratorFlags toDirListingFlags(QDirIterator::IteratorFlags flags)
{
using F = QDirListing::IteratorFlag;
QDirListing::IteratorFlags listerFlags;
if (flags & QDirIterator::NoIteratorFlags)
listerFlags.setFlag(F::Default);
if (flags & QDirIterator::FollowSymlinks)
listerFlags.setFlag(F::FollowDirSymlinks);
if (flags & QDirIterator::Subdirectories)
listerFlags.setFlag(F::Recursive);
return listerFlags;
}
class QDirListingPrivate class QDirListingPrivate
{ {
public: public:
@ -100,15 +164,32 @@ public:
bool matchesFilters(QDirEntryInfo &data) const; bool matchesFilters(QDirEntryInfo &data) const;
bool hasIterators() const; bool hasIterators() const;
bool matchesLegacyFilters(QDirEntryInfo &data) const;
void setLegacyFilters(QDir::Filters dirFilters, QDirIterator::IteratorFlags dirIteratorFlags)
{
useLegacyFilters = true;
legacyDirFilters = dirFilters;
iteratorFlags = toDirListingFlags(dirIteratorFlags);
}
std::unique_ptr<QAbstractFileEngine> engine; std::unique_ptr<QAbstractFileEngine> engine;
QDirEntryInfo initialEntryInfo; QDirEntryInfo initialEntryInfo;
QStringList nameFilters; QStringList nameFilters;
QDir::Filters filters;
QDirListing::IteratorFlags iteratorFlags; QDirListing::IteratorFlags iteratorFlags;
QDirEntryInfo currentEntryInfo; QDirEntryInfo currentEntryInfo;
bool useLegacyFilters = false;
QDir::Filters legacyDirFilters;
#if QT_CONFIG(regularexpression) #if QT_CONFIG(regularexpression)
QList<QRegularExpression> nameRegExps; QList<QRegularExpression> nameRegExps;
bool regexMatchesName(const QString &fileName) const
{
if (nameRegExps.isEmpty())
return true;
auto hasMatch = [&fileName](const auto &re) { return re.match(fileName).hasMatch(); };
return std::any_of(nameRegExps.cbegin(), nameRegExps.cend(), hasMatch);
}
#endif #endif
using FEngineIteratorPtr = std::unique_ptr<QAbstractFileEngineIterator>; using FEngineIteratorPtr = std::unique_ptr<QAbstractFileEngineIterator>;
@ -127,13 +208,21 @@ void QDirListingPrivate::init(bool resolveEngine = true)
if (nameFilters.contains("*"_L1)) if (nameFilters.contains("*"_L1))
nameFilters.clear(); nameFilters.clear();
if (filters == QDir::NoFilter) if (useLegacyFilters) {
filters = QDir::AllEntries; if (legacyDirFilters == QDir::NoFilter)
legacyDirFilters = QDir::AllEntries;
}
#if QT_CONFIG(regularexpression) #if QT_CONFIG(regularexpression)
nameRegExps.reserve(nameFilters.size()); nameRegExps.reserve(nameFilters.size());
const auto cs = filters.testAnyFlags(QDir::CaseSensitive) ? Qt::CaseSensitive
: Qt::CaseInsensitive; const bool isCase = [this] {
if (useLegacyFilters)
return legacyDirFilters.testAnyFlags(QDir::CaseSensitive);
return iteratorFlags.testAnyFlags(QDirListing::IteratorFlag::CaseSensitive);
}();
const auto cs = isCase ? Qt::CaseSensitive : Qt::CaseInsensitive;
for (const auto &filter : nameFilters) for (const auto &filter : nameFilters)
nameRegExps.emplace_back(QRegularExpression::fromWildcard(filter, cs)); nameRegExps.emplace_back(QRegularExpression::fromWildcard(filter, cs));
#endif #endif
@ -171,7 +260,7 @@ void QDirListingPrivate::pushDirectory(QDirEntryInfo &entryInfo)
}(); }();
if (iteratorFlags.testAnyFlags(QDirListing::IteratorFlag::FollowSymlinks)) { if (iteratorFlags.testAnyFlags(QDirListing::IteratorFlag::FollowDirSymlinks)) {
// Stop link loops // Stop link loops
if (visitedLinks.hasSeen(entryInfo.canonicalFilePath())) if (visitedLinks.hasSeen(entryInfo.canonicalFilePath()))
return; return;
@ -179,7 +268,7 @@ void QDirListingPrivate::pushDirectory(QDirEntryInfo &entryInfo)
if (engine) { if (engine) {
engine->setFileName(path); engine->setFileName(path);
if (auto it = engine->beginEntryList(path, filters, nameFilters)) { if (auto it = engine->beginEntryList(path, iteratorFlags, nameFilters)) {
fileEngineIterators.emplace_back(std::move(it)); fileEngineIterators.emplace_back(std::move(it));
} else { } else {
// No iterator; no entry list. // No iterator; no entry list.
@ -191,7 +280,7 @@ void QDirListingPrivate::pushDirectory(QDirEntryInfo &entryInfo)
fentry = &entryInfo.fileInfoOpt->d_ptr->fileEntry; fentry = &entryInfo.fileInfoOpt->d_ptr->fileEntry;
else else
fentry = &entryInfo.entry; fentry = &entryInfo.entry;
nativeIterators.emplace_back(std::make_unique<QFileSystemIterator>(*fentry, filters)); nativeIterators.emplace_back(std::make_unique<QFileSystemIterator>(*fentry, iteratorFlags));
#else #else
qWarning("Qt was built with -no-feature-filesystemiterator: no files/plugins will be found!"); qWarning("Qt was built with -no-feature-filesystemiterator: no files/plugins will be found!");
#endif #endif
@ -201,6 +290,8 @@ void QDirListingPrivate::pushDirectory(QDirEntryInfo &entryInfo)
bool QDirListingPrivate::entryMatches(QDirEntryInfo &entryInfo) bool QDirListingPrivate::entryMatches(QDirEntryInfo &entryInfo)
{ {
checkAndPushDirectory(entryInfo); checkAndPushDirectory(entryInfo);
if (useLegacyFilters)
return matchesLegacyFilters(entryInfo);
return matchesFilters(entryInfo); return matchesFilters(entryInfo);
} }
@ -260,6 +351,11 @@ void QDirListingPrivate::advance()
} }
} }
static bool isDotOrDotDot(QStringView fileName)
{
return fileName == "."_L1 || fileName == ".."_L1;
}
void QDirListingPrivate::checkAndPushDirectory(QDirEntryInfo &entryInfo) void QDirListingPrivate::checkAndPushDirectory(QDirEntryInfo &entryInfo)
{ {
using F = QDirListing::IteratorFlag; using F = QDirListing::IteratorFlag;
@ -272,16 +368,20 @@ void QDirListingPrivate::checkAndPushDirectory(QDirEntryInfo &entryInfo)
return; return;
// Follow symlinks only when asked // Follow symlinks only when asked
if (!iteratorFlags.testAnyFlags(F::FollowSymlinks) && entryInfo.isSymLink()) if (!iteratorFlags.testAnyFlags(F::FollowDirSymlinks) && entryInfo.isSymLink())
return; return;
// Never follow . and .. // Never follow . and ..
const QString &fileName = entryInfo.fileName(); if (isDotOrDotDot(entryInfo.fileName()))
if ("."_L1 == fileName || ".."_L1 == fileName)
return; return;
// No hidden directories unless requested // No hidden directories unless requested
if (!filters.testAnyFlags(QDir::AllDirs | QDir::Hidden) && entryInfo.isHidden()) const bool includeHidden = [this]() {
if (useLegacyFilters)
return legacyDirFilters.testAnyFlags(QDir::AllDirs | QDir::Hidden);
return iteratorFlags.testAnyFlags(QDirListing::IteratorFlag::IncludeHidden);
}();
if (!includeHidden && entryInfo.isHidden())
return; return;
pushDirectory(entryInfo); pushDirectory(entryInfo);
@ -290,21 +390,21 @@ void QDirListingPrivate::checkAndPushDirectory(QDirEntryInfo &entryInfo)
/*! /*!
\internal \internal
This functions returns \c true if the current entry matches the filters Works the same as matchesFilters() but for the old QDir::Filters.
(i.e., the current entry will be returned as part of the directory
iteration); otherwise, \c false is returned.
*/ */
bool QDirListingPrivate::matchesFilters(QDirEntryInfo &entryInfo) const bool QDirListingPrivate::matchesLegacyFilters(QDirEntryInfo &entryInfo) const
{ {
Q_ASSERT(useLegacyFilters);
const QString &fileName = entryInfo.fileName(); const QString &fileName = entryInfo.fileName();
if (fileName.isEmpty()) if (fileName.isEmpty())
return false; return false;
auto &filters = legacyDirFilters;
// filter . and ..? // filter . and ..?
const bool dotOrDotDot = isDotOrDotDot(fileName);
const qsizetype fileNameSize = fileName.size(); const qsizetype fileNameSize = fileName.size();
const bool dotOrDotDot = fileName[0] == u'.'
&& ((fileNameSize == 1)
||(fileNameSize == 2 && fileName[1] == u'.'));
if ((filters & QDir::NoDot) && dotOrDotDot && fileNameSize == 1) if ((filters & QDir::NoDot) && dotOrDotDot && fileNameSize == 1)
return false; return false;
if ((filters & QDir::NoDotDot) && dotOrDotDot && fileNameSize == 2) if ((filters & QDir::NoDotDot) && dotOrDotDot && fileNameSize == 2)
@ -313,11 +413,8 @@ bool QDirListingPrivate::matchesFilters(QDirEntryInfo &entryInfo) const
// name filter // name filter
#if QT_CONFIG(regularexpression) #if QT_CONFIG(regularexpression)
// Pass all entries through name filters, except dirs if AllDirs is set // Pass all entries through name filters, except dirs if AllDirs is set
if (!nameRegExps.isEmpty() && !(filters.testAnyFlags(QDir::AllDirs) && entryInfo.isDir())) { if (!(filters.testAnyFlags(QDir::AllDirs) && entryInfo.isDir())) {
auto regexMatchesName = [&fileName](const auto &re) { if (!regexMatchesName(fileName))
return re.match(fileName).hasMatch();
};
if (std::none_of(nameRegExps.cbegin(), nameRegExps.cend(), regexMatchesName))
return false; return false;
} }
#endif #endif
@ -371,6 +468,59 @@ bool QDirListingPrivate::matchesFilters(QDirEntryInfo &entryInfo) const
return true; return true;
} }
/*!
\internal
This function returns \c true if the current entry matches the filters
(i.e., the current entry will be returned as part of the directory
iteration); otherwise, \c false is returned.
*/
bool QDirListingPrivate::matchesFilters(QDirEntryInfo &entryInfo) const
{
using F = QDirListing::IteratorFlag;
const QString &fileName = entryInfo.fileName();
if (fileName.isEmpty())
return false;
if (isDotOrDotDot(fileName)) // All done, other checks below don't matter in this case
return iteratorFlags.testAnyFlags(F::IncludeDotAndDotDot);
// name filter
#if QT_CONFIG(regularexpression)
if (!regexMatchesName(fileName))
return false;
#endif // QT_CONFIG(regularexpression)
if (!iteratorFlags.testAnyFlag(F::IncludeHidden) && entryInfo.isHidden())
return false;
if (entryInfo.isSymLink()) {
// With ResolveSymlinks, we look at the type of the link's target,
// and exclude broken symlinks (where the target doesn't exist).
if (iteratorFlags.testAnyFlag(F::ResolveSymlinks)) {
if (!entryInfo.exists())
return false;
} else if (iteratorFlags.testAnyFlags(F::FilesOnly)
|| iteratorFlags.testAnyFlags(F::DirsOnly)) {
return false; // symlink is not a file or dir
}
}
if (iteratorFlags.testAnyFlag(F::ExcludeSpecial)
&& !entryInfo.isFile() && !entryInfo.isDir() && !entryInfo.isSymLink()) {
return false;
}
if (iteratorFlags.testAnyFlags(F::ExcludeDirs) && entryInfo.isDir())
return false;
if (iteratorFlags.testAnyFlags(F::ExcludeFiles) && entryInfo.isFile())
return false;
return true;
}
bool QDirListingPrivate::hasIterators() const bool QDirListingPrivate::hasIterators() const
{ {
if (engine) if (engine)
@ -384,60 +534,12 @@ bool QDirListingPrivate::hasIterators() const
} }
/*! /*!
Constructs a QDirListing that can iterate over \a dir's entries, using Constructs a QDirListing that can iterate over \a path.
\a dir's name filters and the QDir::Filters set in \a dir. You can pass
options via \a flags to decide how the directory should be iterated.
By default, \a flags is NoIteratorFlags, which provides the same behavior You can pass options via \a flags to control how the directory should
as in QDir::entryList(). be iterated.
The sorting in \a dir is ignored. By default, \a flags is IteratorFlag::Default.
\note To list symlinks that point to non existing files, QDir::System
must be set in \a dir's QDir::Filters.
\sa IteratorFlags
*/
QDirListing::QDirListing(const QDir &dir, IteratorFlags flags)
: d(new QDirListingPrivate)
{
const QDirPrivate *other = dir.d_ptr.constData();
d->initialEntryInfo.entry = other->dirEntry;
d->nameFilters = other->nameFilters;
d->filters = other->filters;
d->iteratorFlags = flags;
const bool resolveEngine = other->fileEngine ? true : false;
d->init(resolveEngine);
}
/*!
Constructs a QDirListing that can iterate over \a path. Entries are
filtered according to \a filters. You can pass options via \a flags to
decide how the directory should be iterated.
By default, \a filters is QDir::NoFilter, and \a flags is NoIteratorFlags,
which provides the same behavior as in QDir::entryList().
\note To list symlinks that point to non existing files, QDir::System
must be set in \a filters.
\sa IteratorFlags
*/
QDirListing::QDirListing(const QString &path, QDir::Filters filters, IteratorFlags flags)
: d(new QDirListingPrivate)
{
d->initialEntryInfo.entry = QFileSystemEntry(path);
d->filters = filters;
d->iteratorFlags = flags;
d->init();
}
/*!
Constructs a QDirListing that can iterate over \a path. You can pass
options via \a flags to decide how the directory should be iterated.
By default, \a flags is NoIteratorFlags, which provides the same behavior
as in QDir::entryList().
\sa IteratorFlags \sa IteratorFlags
*/ */
@ -445,40 +547,57 @@ QDirListing::QDirListing(const QString &path, IteratorFlags flags)
: d(new QDirListingPrivate) : d(new QDirListingPrivate)
{ {
d->initialEntryInfo.entry = QFileSystemEntry(path); d->initialEntryInfo.entry = QFileSystemEntry(path);
d->filters = QDir::NoFilter;
d->iteratorFlags = flags; d->iteratorFlags = flags;
d->init(); d->init();
} }
/*! /*!
Constructs a QDirListing that can iterate over \a path, using \a Constructs a QDirListing that can iterate over \a path.
nameFilters and \a filters. You can pass options via \a flags to decide
how the directory should be iterated.
By default, \a flags is NoIteratorFlags, which provides the same behavior You can pass options via \a flags to control how the directory should
as QDir::entryList(). be iterated. By default, \a flags is IteratorFlag::Default.
The listed entries will be filtered according to the file glob patterns
in \a nameFilters (see QDir::setNameFilters() for more details).
For example, the following iterator could be used to iterate over audio For example, the following iterator could be used to iterate over audio
files: files:
\snippet code/src_corelib_io_qdirlisting.cpp 2 \snippet code/src_corelib_io_qdirlisting.cpp 2
\note To list symlinks that point to non existing files, QDir::System
must be set in \a flags.
\sa IteratorFlags, QDir::setNameFilters() \sa IteratorFlags, QDir::setNameFilters()
*/ */
QDirListing::QDirListing(const QString &path, const QStringList &nameFilters, QDir::Filters filters, QDirListing::QDirListing(const QString &path, const QStringList &nameFilters, IteratorFlags flags)
IteratorFlags flags)
: d(new QDirListingPrivate) : d(new QDirListingPrivate)
{ {
d->initialEntryInfo.entry = QFileSystemEntry(path); d->initialEntryInfo.entry = QFileSystemEntry(path);
d->nameFilters = nameFilters; d->nameFilters = nameFilters;
d->filters = filters;
d->iteratorFlags = flags; d->iteratorFlags = flags;
d->init(); d->init();
} }
/*!
\internal
Only used by classes that still have to use QDir::Filters; for example,
QDir, such usage may be deprecated at some point.
\a qdirFilters is converted to QDir::Filters and \a qdirIteratorFlags is
converted to QDirIterator::IteratorFlags (qdirlisting.h can't include
qdir.h or qdiriterator.h) and used to control the filtering of the
dir entries.
*/
QDirListing::QDirListing(const QString &path, const QStringList &nameFilters, uint qdirFilters,
uint qdirIteratorFlags)
: d(new QDirListingPrivate)
{
d->initialEntryInfo.entry = QFileSystemEntry(path);
d->nameFilters = nameFilters;
d->setLegacyFilters(QDir::Filters::fromInt(qdirFilters),
QDirIterator::IteratorFlags::fromInt(qdirIteratorFlags));
d->init();
}
/*! /*!
Move constructor. Moves \a other into this QDirListing. Move constructor. Moves \a other into this QDirListing.
@ -497,6 +616,7 @@ QDirListing::QDirListing(QDirListing &&other) = default;
*/ */
QDirListing &QDirListing::operator=(QDirListing &&other) = default; QDirListing &QDirListing::operator=(QDirListing &&other) = default;
/*! /*!
Destroys the QDirListing. Destroys the QDirListing.
*/ */

View File

@ -5,7 +5,11 @@
#ifndef QDILISTING_H #ifndef QDILISTING_H
#define QDILISTING_H #define QDILISTING_H
#include <QtCore/qdir.h> #include <QtCore/qfiledevice.h>
#include <QtCore/qflags.h>
#include <QtCore/qtclasshelpermacros.h>
#include <QtCore/qtcoreexports.h>
#include <QtCore/qdatetime.h>
#include <iterator> #include <iterator>
#include <memory> #include <memory>
@ -13,24 +17,32 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDirListingPrivate; class QDirListingPrivate;
class QFileInfo;
class QDir;
class QTimeZone;
class Q_CORE_EXPORT QDirListing class Q_CORE_EXPORT QDirListing
{ {
public: public:
enum class IteratorFlag { enum class IteratorFlag {
NoFlag = 0x0, Default = 0x000000,
FollowSymlinks = 0x1, ExcludeFiles = 0x000004,
Recursive = 0x2 ExcludeDirs = 0x000008,
ExcludeSpecial = 0x000010,
ResolveSymlinks = 0x000020,
FilesOnly = ExcludeDirs | ExcludeSpecial,
DirsOnly = ExcludeFiles | ExcludeSpecial,
IncludeHidden = 0x000040,
IncludeDotAndDotDot = 0x000080,
CaseSensitive = 0x000100,
Recursive = 0x000400,
FollowDirSymlinks = 0x000800,
}; };
Q_DECLARE_FLAGS(IteratorFlags, IteratorFlag) Q_DECLARE_FLAGS(IteratorFlags, IteratorFlag)
QDirListing(const QDir &dir, IteratorFlags flags = IteratorFlag::NoFlag); explicit QDirListing(const QString &path, IteratorFlags flags = IteratorFlag::Default);
QDirListing(const QString &path, IteratorFlags flags = IteratorFlag::NoFlag); explicit QDirListing(const QString &path, const QStringList &nameFilters,
QDirListing(const QString &path, QDir::Filters filter, IteratorFlags flags = IteratorFlag::Default);
IteratorFlags flags = IteratorFlag::NoFlag);
QDirListing(const QString &path, const QStringList &nameFilters,
QDir::Filters filters = QDir::NoFilter,
IteratorFlags flags = IteratorFlag::NoFlag);
QDirListing(QDirListing &&); QDirListing(QDirListing &&);
QDirListing &operator=(QDirListing &&); QDirListing &operator=(QDirListing &&);
@ -65,11 +77,15 @@ public:
QString absolutePath() const; QString absolutePath() const;
qint64 size() const; qint64 size() const;
QDateTime birthTime(const QTimeZone &tz) const { return fileTime(QFile::FileBirthTime, tz); } QDateTime birthTime(const QTimeZone &tz) const
QDateTime metadataChangeTime(const QTimeZone &tz) const { return fileTime(QFile::FileMetadataChangeTime, tz); } { return fileTime(QFileDevice::FileBirthTime, tz); }
QDateTime lastModified(const QTimeZone &tz) const { return fileTime(QFile::FileModificationTime, tz); } QDateTime metadataChangeTime(const QTimeZone &tz) const
QDateTime lastRead(const QTimeZone &tz) const { return fileTime(QFile::FileAccessTime, tz); } { return fileTime(QFileDevice::FileMetadataChangeTime, tz); }
QDateTime fileTime(QFile::FileTime type, const QTimeZone &tz) const; QDateTime lastModified(const QTimeZone &tz) const
{ return fileTime(QFileDevice::FileModificationTime, tz); }
QDateTime lastRead(const QTimeZone &tz) const
{ return fileTime(QFileDevice::FileAccessTime, tz); }
QDateTime fileTime(QFileDevice::FileTime type, const QTimeZone &tz) const;
}; };
class const_iterator class const_iterator
@ -111,8 +127,19 @@ public:
private: private:
Q_DISABLE_COPY(QDirListing) Q_DISABLE_COPY(QDirListing)
// Private constructor that is used in deprecated code paths.
// `uint` instead of QDir::Filters and QDirIterator::IteratorFlags
// because qdir.h can't be included here; qdiriterator.h can't included
// either, because it includes qdir.h
QDirListing(const QString &path, const QStringList &nameFilters, uint dirFilters,
uint qdirIteratorFlags = 0); // QDirIterator::NoIteratorFlags == 0x0
std::unique_ptr<QDirListingPrivate> d; std::unique_ptr<QDirListingPrivate> d;
friend class QDir; friend class QDir;
friend class QDirPrivate;
friend class QDirIteratorPrivate;
friend class QAbstractFileEngine;
friend class QFileInfoGatherer;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(QDirListing::IteratorFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(QDirListing::IteratorFlags)

View File

@ -38,6 +38,7 @@ class QFileSystemIterator
public: public:
QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters); QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters);
QFileSystemIterator(const QFileSystemEntry &entry); QFileSystemIterator(const QFileSystemEntry &entry);
QFileSystemIterator(const QFileSystemEntry &entry, QDirListing::IteratorFlags filters);
~QFileSystemIterator(); ~QFileSystemIterator();
bool advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData); bool advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData);

View File

@ -32,6 +32,10 @@ QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry)
} }
} }
QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDirListing::IteratorFlags)
: QFileSystemIterator(entry)
{}
QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters) QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters)
: QFileSystemIterator(entry) : QFileSystemIterator(entry)
{ {

View File

@ -42,6 +42,13 @@ QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Fi
onlyDirs = true; onlyDirs = true;
} }
QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry,
QDirListing::IteratorFlags flags)
: QFileSystemIterator(entry)
{
onlyDirs = flags.testAnyFlags(QDirListing::IteratorFlag::DirsOnly);
}
QFileSystemIterator::~QFileSystemIterator() QFileSystemIterator::~QFileSystemIterator()
{ {
if (findFileHandle != INVALID_HANDLE_VALUE) if (findFileHandle != INVALID_HANDLE_VALUE)

View File

@ -798,6 +798,12 @@ QFSFileEngine::beginEntryList(const QString &path, QDir::Filters filters,
return std::make_unique<QFSFileEngineIterator>(path, filters, filterNames); return std::make_unique<QFSFileEngineIterator>(path, filters, filterNames);
} }
QAbstractFileEngine::IteratorUniquePtr
QFSFileEngine::beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
{
return std::make_unique<QFSFileEngineIterator>(path, filters, filterNames);
}
#endif // QT_NO_FILESYSTEMITERATOR #endif // QT_NO_FILESYSTEMITERATOR
/*! /*!

View File

@ -16,6 +16,13 @@ QFSFileEngineIterator::QFSFileEngineIterator(const QString &path, QDir::Filters
{ {
} }
QFSFileEngineIterator::QFSFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
: QAbstractFileEngineIterator(path, filters, filterNames),
nativeIterator(new QFileSystemIterator(QFileSystemEntry(path), filters))
{
}
QFSFileEngineIterator::~QFSFileEngineIterator() QFSFileEngineIterator::~QFSFileEngineIterator()
{ {
} }

View File

@ -27,6 +27,8 @@ class QFSFileEngineIterator : public QAbstractFileEngineIterator
{ {
public: public:
QFSFileEngineIterator(const QString &path, QDir::Filters filters, const QStringList &filterNames); QFSFileEngineIterator(const QString &path, QDir::Filters filters, const QStringList &filterNames);
QFSFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames);
~QFSFileEngineIterator(); ~QFSFileEngineIterator();
bool advance() override; bool advance() override;

View File

@ -88,6 +88,8 @@ public:
#ifndef QT_NO_FILESYSTEMITERATOR #ifndef QT_NO_FILESYSTEMITERATOR
IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters,
const QStringList &filterNames) override; const QStringList &filterNames) override;
IteratorUniquePtr beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames) override;
#endif #endif
qint64 read(char *data, qint64 maxlen) override; qint64 read(char *data, qint64 maxlen) override;

View File

@ -1589,6 +1589,13 @@ QResourceFileEngine::beginEntryList(const QString &path, QDir::Filters filters,
return std::make_unique<QResourceFileEngineIterator>(path, filters, filterNames); return std::make_unique<QResourceFileEngineIterator>(path, filters, filterNames);
} }
QAbstractFileEngine::IteratorUniquePtr
QResourceFileEngine::beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
{
return std::make_unique<QResourceFileEngineIterator>(path, filters, filterNames);
}
bool QResourceFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) bool QResourceFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
{ {
Q_D(QResourceFileEngine); Q_D(QResourceFileEngine);

View File

@ -15,6 +15,14 @@ QResourceFileEngineIterator::QResourceFileEngineIterator(const QString &path, QD
{ {
} }
QResourceFileEngineIterator::QResourceFileEngineIterator(const QString &path,
QDirListing::IteratorFlags filters,
const QStringList &filterNames)
: QAbstractFileEngineIterator(path, filters, filterNames),
index(-1)
{
}
QResourceFileEngineIterator::~QResourceFileEngineIterator() QResourceFileEngineIterator::~QResourceFileEngineIterator()
{ {
} }

View File

@ -26,6 +26,8 @@ class QResourceFileEngineIterator : public QAbstractFileEngineIterator
public: public:
QResourceFileEngineIterator(const QString &path, QDir::Filters filters, QResourceFileEngineIterator(const QString &path, QDir::Filters filters,
const QStringList &filterNames); const QStringList &filterNames);
QResourceFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames);
~QResourceFileEngineIterator(); ~QResourceFileEngineIterator();
bool advance() override; bool advance() override;

View File

@ -51,6 +51,8 @@ public:
IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters,
const QStringList &filterNames) override; const QStringList &filterNames) override;
IteratorUniquePtr beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames) override;
bool extension(Extension extension, const ExtensionOption *option = nullptr, ExtensionReturn *output = nullptr) override; bool extension(Extension extension, const ExtensionOption *option = nullptr, ExtensionReturn *output = nullptr) override;
bool supportsExtension(Extension extension) const override; bool supportsExtension(Extension extension) const override;

View File

@ -140,9 +140,7 @@ static inline quint64 retrieveDeviceId(const QByteArray &device, quint64 deviceI
static QDirListing devicesByLabel() static QDirListing devicesByLabel()
{ {
static const char pathDiskByLabel[] = "/dev/disk/by-label"; static const char pathDiskByLabel[] = "/dev/disk/by-label";
static constexpr auto LabelFileFilter = static constexpr auto LabelFileFilter = QDirListing::IteratorFlag::IncludeHidden;
QDir::AllEntries | QDir::System | QDir::Hidden | QDir::NoDotAndDotDot;
return QDirListing(QLatin1StringView(pathDiskByLabel), LabelFileFilter); return QDirListing(QLatin1StringView(pathDiskByLabel), LabelFileFilter);
} }

View File

@ -309,7 +309,7 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
#elif defined(Q_OS_ANDROID) #elif defined(Q_OS_ANDROID)
QStringList("libplugins_%1_*.so"_L1.arg(suffix)), QStringList("libplugins_%1_*.so"_L1.arg(suffix)),
#endif #endif
QDir::Files); QDirListing::IteratorFlag::FilesOnly);
for (const auto &dirEntry : plugins) { for (const auto &dirEntry : plugins) {
const QString &fileName = dirEntry.fileName(); const QString &fileName = dirEntry.fileName();

View File

@ -11,6 +11,7 @@
#include <QtCore/QDataStream> #include <QtCore/QDataStream>
#include <QtCore/QDateTime> #include <QtCore/QDateTime>
#include <QtCore/QDirListing> #include <QtCore/QDirListing>
#include <QtCore/QDir>
#include <QtCore/QFile> #include <QtCore/QFile>
#include <QtCore/QCache> #include <QtCore/QCache>
#include <QtCore/QMap> #include <QtCore/QMap>
@ -113,14 +114,16 @@ static QTzTimeZoneHash loadTzTimeZones()
} }
} }
const QString path = tzif.fileName(); QString path = tzif.fileName();
const qsizetype cut = path.lastIndexOf(u'/'); const qsizetype cut = path.lastIndexOf(u'/');
Q_ASSERT(cut > 0); Q_ASSERT(cut > 0);
const QDir zoneDir = QDir(path.first(cut)); path.truncate(cut + 1);
for (const auto &info : QDirListing(zoneDir, QDirListing::IteratorFlag::Recursive)) { const qsizetype prefixLen = path.size();
for (const auto &info : QDirListing(path, QDirListing::IteratorFlag::Recursive)) {
if (!(info.isFile() || info.isSymLink())) if (!(info.isFile() || info.isSymLink()))
continue; continue;
const QString name = zoneDir.relativeFilePath(info.filePath()); const QString infoAbsolutePath = info.absoluteFilePath();
const QString name = infoAbsolutePath.sliced(prefixLen);
// Two sub-directories containing (more or less) copies of the zoneinfo tree. // Two sub-directories containing (more or less) copies of the zoneinfo tree.
if (info.isDir() ? name == "posix"_L1 || name == "right"_L1 if (info.isDir() ? name == "posix"_L1 || name == "right"_L1
: name.startsWith("posix/"_L1) || name.startsWith("right/"_L1)) { : name.startsWith("posix/"_L1) || name.startsWith("right/"_L1)) {
@ -130,7 +133,7 @@ static QTzTimeZoneHash loadTzTimeZones()
// isTzFile() check; in practice current (2023) zoneinfo/ contains only // isTzFile() check; in practice current (2023) zoneinfo/ contains only
// actual zone files and matches to that filter. // actual zone files and matches to that filter.
const QByteArray id = QFile::encodeName(name); const QByteArray id = QFile::encodeName(name);
if (!zonesHash.contains(id) && isTzFile(zoneDir.absoluteFilePath(name))) if (!zonesHash.contains(id) && isTzFile(infoAbsolutePath))
zonesHash.insert(id, QTzTimeZone()); zonesHash.insert(id, QTzTimeZone());
} }
return zonesHash; return zonesHash;

View File

@ -419,8 +419,10 @@ void QFileInfoGatherer::getFileInfos(const QString &path, const QStringList &fil
QStringList allFiles; QStringList allFiles;
if (files.isEmpty()) { if (files.isEmpty()) {
// Use QDirListing::IteratorFlags when QFileSystemModel is
// changed to use them too
constexpr auto dirFilters = QDir::AllEntries | QDir::System | QDir::Hidden; constexpr auto dirFilters = QDir::AllEntries | QDir::System | QDir::Hidden;
for (const auto &dirEntry : QDirListing(path, dirFilters)) { for (const auto &dirEntry : QDirListing(path, {}, dirFilters.toInt())) {
if (isInterruptionRequested()) if (isInterruptionRequested())
break; break;
fileInfo = dirEntry.fileInfo(); fileInfo = dirEntry.fileInfo();

View File

@ -473,8 +473,6 @@ qint64 QNetworkDiskCache::expire()
// close file handle to prevent "in use" error when QFile::remove() is called // close file handle to prevent "in use" error when QFile::remove() is called
d->lastItem.reset(); d->lastItem.reset();
const QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
struct CacheItem struct CacheItem
{ {
std::chrono::milliseconds msecs; std::chrono::milliseconds msecs;
@ -484,7 +482,7 @@ qint64 QNetworkDiskCache::expire()
std::vector<CacheItem> cacheItems; std::vector<CacheItem> cacheItems;
qint64 totalSize = 0; qint64 totalSize = 0;
using F = QDirListing::IteratorFlag; using F = QDirListing::IteratorFlag;
for (const auto &dirEntry : QDirListing(cacheDirectory(), filters, F::Recursive)) { for (const auto &dirEntry : QDirListing(cacheDirectory(), F::FilesOnly | F::Recursive)) {
if (!dirEntry.fileName().endsWith(CACHE_POSTFIX)) if (!dirEntry.fileName().endsWith(CACHE_POSTFIX))
continue; continue;

View File

@ -681,8 +681,8 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
#endif #endif
using F = QDirListing::IteratorFlag; using F = QDirListing::IteratorFlag;
constexpr auto iterFlags = F::FollowSymlinks | F::Recursive; constexpr auto iterFlags = F::FollowDirSymlinks | F::Recursive | F::FilesOnly;
for (const auto &dirEntry : QDirListing(pathPrefixString, QDir::Files, iterFlags)) { for (const auto &dirEntry : QDirListing(pathPrefixString, iterFlags)) {
QString filePath = dirEntry.filePath(); QString filePath = dirEntry.filePath();
if (startIndex > 0) if (startIndex > 0)
filePath.remove(0, startIndex); filePath.remove(0, startIndex);

View File

@ -245,7 +245,7 @@ QString AndroidContentFileEngine::fileName(FileName f) const
} }
QAbstractFileEngine::IteratorUniquePtr QAbstractFileEngine::IteratorUniquePtr
AndroidContentFileEngine::beginEntryList(const QString &path, QDir::Filters filters, AndroidContentFileEngine::beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames) const QStringList &filterNames)
{ {
return std::make_unique<AndroidContentFileEngineIterator>(path, filters, filterNames); return std::make_unique<AndroidContentFileEngineIterator>(path, filters, filterNames);
@ -265,7 +265,7 @@ AndroidContentFileEngineHandler::create(const QString &fileName) const
} }
AndroidContentFileEngineIterator::AndroidContentFileEngineIterator( AndroidContentFileEngineIterator::AndroidContentFileEngineIterator(
const QString &path, QDir::Filters filters, const QStringList &filterNames) const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
: QAbstractFileEngineIterator(path, filters, filterNames) : QAbstractFileEngineIterator(path, filters, filterNames)
{ {
} }

View File

@ -30,7 +30,7 @@ public:
QDateTime fileTime(QFile::FileTime time) const override; QDateTime fileTime(QFile::FileTime time) const override;
FileFlags fileFlags(FileFlags type = FileInfoAll) const override; FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
QString fileName(FileName file = DefaultName) const override; QString fileName(FileName file = DefaultName) const override;
IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, IteratorUniquePtr beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames) override; const QStringList &filterNames) override;
private: private:
@ -53,7 +53,7 @@ public:
class AndroidContentFileEngineIterator : public QAbstractFileEngineIterator class AndroidContentFileEngineIterator : public QAbstractFileEngineIterator
{ {
public: public:
AndroidContentFileEngineIterator(const QString &path, QDir::Filters filters, AndroidContentFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames); const QStringList &filterNames);
~AndroidContentFileEngineIterator(); ~AndroidContentFileEngineIterator();

View File

@ -164,7 +164,7 @@ Q_CONSTINIT QMutex FolderIterator::m_assetsCacheMutex;
class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator
{ {
public: public:
AndroidAbstractFileEngineIterator(QDir::Filters filters, AndroidAbstractFileEngineIterator(QDirListing::IteratorFlags filters,
const QStringList &nameFilters, const QStringList &nameFilters,
const QString &path) const QString &path)
: QAbstractFileEngineIterator(path, filters, nameFilters) : QAbstractFileEngineIterator(path, filters, nameFilters)
@ -351,8 +351,8 @@ public:
m_assetsInfoCache.insert(m_fileName, newAssetInfoPtr); m_assetsInfoCache.insert(m_fileName, newAssetInfoPtr);
} }
IteratorUniquePtr IteratorUniquePtr beginEntryList(const QString &, QDirListing::IteratorFlags filters,
beginEntryList(const QString &, QDir::Filters filters, const QStringList &filterNames) override const QStringList &filterNames) override
{ {
// AndroidAbstractFileEngineIterator use `m_fileName` as the path // AndroidAbstractFileEngineIterator use `m_fileName` as the path
if (m_assetInfo && m_assetInfo->type == AssetItem::Type::Folder) if (m_assetInfo && m_assetInfo->type == AssetItem::Type::Folder)

View File

@ -29,7 +29,7 @@ public:
void setFileName(const QString &file) override; void setFileName(const QString &file) override;
#ifndef QT_NO_FILESYSTEMITERATOR #ifndef QT_NO_FILESYSTEMITERATOR
IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, IteratorUniquePtr beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames) override; const QStringList &filterNames) override;
#endif #endif

View File

@ -258,7 +258,7 @@ public:
QIOSAssetEnumerator *m_enumerator; QIOSAssetEnumerator *m_enumerator;
QIOSFileEngineIteratorAssetsLibrary( QIOSFileEngineIteratorAssetsLibrary(
const QString &path, QDir::Filters filters, const QStringList &nameFilters) const QString &path, QDirListing::IteratorFlags filters, const QStringList &nameFilters)
: QAbstractFileEngineIterator(path, filters, nameFilters) : QAbstractFileEngineIterator(path, filters, nameFilters)
, m_enumerator(new QIOSAssetEnumerator([[[ALAssetsLibrary alloc] init] autorelease], ALAssetsGroupAll)) , m_enumerator(new QIOSAssetEnumerator([[[ALAssetsLibrary alloc] init] autorelease], ALAssetsGroupAll))
{ {
@ -440,7 +440,7 @@ void QIOSFileEngineAssetsLibrary::setFileName(const QString &file)
QAbstractFileEngine::IteratorUniquePtr QAbstractFileEngine::IteratorUniquePtr
QIOSFileEngineAssetsLibrary::beginEntryList( QIOSFileEngineAssetsLibrary::beginEntryList(
const QString &path, QDir::Filters filters, const QStringList &filterNames) const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
{ {
return std::make_unique<QIOSFileEngineIteratorAssetsLibrary>(path, filters, filterNames); return std::make_unique<QIOSFileEngineIteratorAssetsLibrary>(path, filters, filterNames);
} }

View File

@ -207,7 +207,8 @@ void QTlsBackendOpenSSL::ensureCiphersAndCertsLoaded() const
const QStringList symLinkFilter{ const QStringList symLinkFilter{
u"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]"_s}; u"[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].[0-9]"_s};
for (const auto &dir : dirs) { for (const auto &dir : dirs) {
QDirListing dirList(QString::fromLatin1(dir), symLinkFilter, QDir::Files); QDirListing dirList(QString::fromLatin1(dir), symLinkFilter,
QDirListing::IteratorFlag::FilesOnly);
if (dirList.cbegin() != dirList.cend()) { // Not empty if (dirList.cbegin() != dirList.cend()) { // Not empty
QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true); QSslSocketPrivate::setRootCertOnDemandLoadingSupported(true);
break; break;
@ -390,11 +391,9 @@ QList<QSslCertificate> systemCaCertificates()
QStringLiteral("/etc/pki/tls/certs/ca-bundle.crt"), // Fedora, Mandriva QStringLiteral("/etc/pki/tls/certs/ca-bundle.crt"), // Fedora, Mandriva
QStringLiteral("/usr/local/share/certs/ca-root-nss.crt") // FreeBSD's ca_root_nss QStringLiteral("/usr/local/share/certs/ca-root-nss.crt") // FreeBSD's ca_root_nss
}; };
QDir currentDir; static const QStringList nameFilters = {u"*.pem"_s, u"*.crt"_s};
currentDir.setNameFilters(QStringList{QStringLiteral("*.pem"), QStringLiteral("*.crt")});
for (const auto &directory : directories) { for (const auto &directory : directories) {
currentDir.setPath(QLatin1StringView(directory)); for (const auto &dirEntry : QDirListing(directory, nameFilters)) {
for (const auto &dirEntry : QDirListing(currentDir)) {
// use canonical path here to not load the same certificate twice if symlinked // use canonical path here to not load the same certificate twice if symlinked
certFiles.insert(dirEntry.canonicalFilePath()); certFiles.insert(dirEntry.canonicalFilePath());
} }

View File

@ -2870,7 +2870,7 @@ void checkAndWarnGradleLongPaths(const QString &outputDirectory)
QStringList longFileNames; QStringList longFileNames;
using F = QDirListing::IteratorFlag; using F = QDirListing::IteratorFlag;
for (const auto &dirEntry : QDirListing(outputDirectory, QStringList(u"*.java"_s), for (const auto &dirEntry : QDirListing(outputDirectory, QStringList(u"*.java"_s),
QDir::Files, F::Recursive)) { F::FilesOnly | F::Recursive)) {
if (dirEntry.size() >= MAX_PATH) if (dirEntry.size() >= MAX_PATH)
longFileNames.append(dirEntry.filePath()); longFileNames.append(dirEntry.filePath());
} }

View File

@ -635,13 +635,13 @@ bool RCCResourceLibrary::interpretResourceFile(QIODevice *inputDevice,
absFileName.prepend(currentPath); absFileName.prepend(currentPath);
QFileInfo file(absFileName); QFileInfo file(absFileName);
if (file.isDir()) { if (file.isDir()) {
QDir dir(file.filePath());
if (!alias.endsWith(slash)) if (!alias.endsWith(slash))
alias += slash; alias += slash;
QStringList filePaths; QStringList filePaths;
using F = QDirListing::IteratorFlag; using F = QDirListing::IteratorFlag;
for (const auto &entry : QDirListing(dir, F::FollowSymlinks | F::Recursive)) { constexpr auto flags = F::FollowDirSymlinks | F::Recursive;
for (const auto &entry : QDirListing(file.filePath(), flags)) {
const QString &fileName = entry.fileName(); const QString &fileName = entry.fileName();
if (fileName == "."_L1 || fileName == ".."_L1) if (fileName == "."_L1 || fileName == ".."_L1)
continue; continue;

View File

@ -133,8 +133,9 @@ bool QtModuleInfoStore::populate(const QString &modulesDir, const QString &trans
} }
} }
using F = QDirListing::IteratorFlag;
// Read modules, and assign a bit as ID. // Read modules, and assign a bit as ID.
for (const auto &dirEntry : QDirListing(modulesDir, {u"*.json"_s}, QDir::Files)) { for (const auto &dirEntry : QDirListing(modulesDir, {u"*.json"_s}, F::FilesOnly)) {
QtModule module = moduleFromJsonFile(dirEntry.filePath(), errorString); QtModule module = moduleFromJsonFile(dirEntry.filePath(), errorString);
if (!errorString->isEmpty()) if (!errorString->isEmpty())
return false; return false;

View File

@ -432,7 +432,7 @@ public:
class Iterator : public QAbstractFileEngineIterator class Iterator : public QAbstractFileEngineIterator
{ {
public: public:
Iterator(const QString &path, QDir::Filters filters, const QStringList &filterNames) Iterator(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
: QAbstractFileEngineIterator(path, filters, filterNames) : QAbstractFileEngineIterator(path, filters, filterNames)
{ {
names.append("foo"); names.append("foo");
@ -463,8 +463,8 @@ public:
{ {
} }
IteratorUniquePtr IteratorUniquePtr beginEntryList(const QString &path, QDirListing::IteratorFlags filters,
beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames) override const QStringList &filterNames) override
{ {
return std::make_unique<Iterator>(path, filters, filterNames); return std::make_unique<Iterator>(path, filters, filterNames);
} }

View File

@ -26,7 +26,6 @@
using namespace Qt::StringLiterals; using namespace Qt::StringLiterals;
Q_DECLARE_METATYPE(QDirListing::IteratorFlags) Q_DECLARE_METATYPE(QDirListing::IteratorFlags)
Q_DECLARE_METATYPE(QDir::Filters)
using ItFlag = QDirListing::IteratorFlag; using ItFlag = QDirListing::IteratorFlag;
@ -187,183 +186,259 @@ void tst_QDirListing::iterateRelativeDirectory_data()
{ {
QTest::addColumn<QString>("dirName"); // relative from current path or abs QTest::addColumn<QString>("dirName"); // relative from current path or abs
QTest::addColumn<QDirListing::IteratorFlags>("flags"); QTest::addColumn<QDirListing::IteratorFlags>("flags");
QTest::addColumn<QDir::Filters>("filters");
QTest::addColumn<QStringList>("nameFilters"); QTest::addColumn<QStringList>("nameFilters");
QTest::addColumn<QStringList>("entries"); QTest::addColumn<QStringList>("entries");
QTest::newRow("no flags") const QStringList allSymlinks = {
<< QString("entrylist") << QDirListing::IteratorFlags{} #if !defined(Q_NO_SYMLINKS)
<< QDir::Filters(QDir::NoFilter) << QStringList("*") "entrylist/linktofile.lnk"_L1,
<< QString( "entrylist/brokenlink.lnk"_L1,
"entrylist/.," # if !defined(Q_NO_SYMLINKS_TO_DIRS)
"entrylist/..," "entrylist/linktodirectory.lnk"_L1,
"entrylist/file," # endif
#ifndef Q_NO_SYMLINKS
"entrylist/linktofile.lnk,"
#endif #endif
"entrylist/directory," };
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
"entrylist/linktodirectory.lnk,"
#endif
"entrylist/writable").split(',');
QTest::newRow("NoDot") const QStringList nonBrokenSymlinks = {
<< QString("entrylist") << QDirListing::IteratorFlags{} #if !defined(Q_NO_SYMLINKS)
<< QDir::Filters(QDir::AllEntries | QDir::NoDot) << QStringList("*") "entrylist/linktofile.lnk"_L1,
<< QString( # if !defined(Q_NO_SYMLINKS_TO_DIRS)
"entrylist/..," "entrylist/linktodirectory.lnk"_L1,
"entrylist/file," # endif
#ifndef Q_NO_SYMLINKS
"entrylist/linktofile.lnk,"
#endif #endif
"entrylist/directory," };
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
"entrylist/linktodirectory.lnk,"
#endif
"entrylist/writable").split(',');
QTest::newRow("NoDotDot") using F = QDirListing::IteratorFlag;
<< QString("entrylist") << QDirListing::IteratorFlags{} QTest::newRow("Default_Flag")
<< QDir::Filters(QDir::AllEntries | QDir::NoDotDot) << QStringList("*") << QString("entrylist") << QDirListing::IteratorFlags{F::Default}
<< QString( << QStringList("*")
"entrylist/.," << QStringList{
"entrylist/file," "entrylist/file"_L1,
#ifndef Q_NO_SYMLINKS "entrylist/directory"_L1,
"entrylist/linktofile.lnk," "entrylist/writable"_L1,
#endif } + allSymlinks;
"entrylist/directory,"
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
"entrylist/linktodirectory.lnk,"
#endif
"entrylist/writable").split(',');
QTest::newRow("NoDotAndDotDot") QTest::newRow("IncludeDotAndDotDot")
<< QString("entrylist") << QDirListing::IteratorFlags{} << QString("entrylist")
<< QDir::Filters(QDir::AllEntries | QDir::NoDotAndDotDot) << QStringList("*") << QDirListing::IteratorFlags{F::IncludeDotAndDotDot}
<< QString( << QStringList("*")
"entrylist/file," << QStringList{
#ifndef Q_NO_SYMLINKS "entrylist/."_L1,
"entrylist/linktofile.lnk," "entrylist/.."_L1,
#endif "entrylist/file"_L1,
"entrylist/directory," "entrylist/directory"_L1,
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS) "entrylist/writable"_L1,
"entrylist/linktodirectory.lnk," } + allSymlinks;
#endif
"entrylist/writable").split(',');
QTest::newRow("QDir::Subdirectories | QDir::FollowSymlinks") QTest::newRow("Recursive-IncludeDotAndDotDot")
<< QString("entrylist") << QDirListing::IteratorFlags(ItFlag::Recursive | ItFlag::FollowSymlinks) << QString("entrylist")
<< QDir::Filters(QDir::NoFilter) << QStringList("*") << QDirListing::IteratorFlags{F::Recursive | F::IncludeDotAndDotDot}
<< QString( << QStringList("*")
"entrylist/.," << QStringList{
"entrylist/..," "entrylist/."_L1,
"entrylist/directory/.," "entrylist/.."_L1,
"entrylist/directory/..," "entrylist/file"_L1,
"entrylist/file," "entrylist/directory"_L1,
#ifndef Q_NO_SYMLINKS "entrylist/directory/."_L1,
"entrylist/linktofile.lnk," "entrylist/directory/.."_L1,
#endif "entrylist/directory/dummy"_L1,
"entrylist/directory," "entrylist/writable"_L1,
"entrylist/directory/dummy," } + allSymlinks;
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
"entrylist/linktodirectory.lnk,"
#endif
"entrylist/writable").split(',');
QTest::newRow("QDir::Subdirectories / QDir::Files") QTest::newRow("Recursive")
<< QString("entrylist") << QDirListing::IteratorFlags(ItFlag::Recursive) << QString("entrylist")
<< QDir::Filters(QDir::Files) << QStringList("*") << QDirListing::IteratorFlags(F::Recursive)
<< QString("entrylist/directory/dummy," << QStringList("*")
"entrylist/file," << QStringList{
#ifndef Q_NO_SYMLINKS "entrylist/file"_L1,
"entrylist/linktofile.lnk," "entrylist/directory"_L1,
"entrylist/directory/dummy"_L1,
"entrylist/writable"_L1,
} + allSymlinks;
QTest::newRow("ResolveSymlinks")
<< QString("entrylist")
<< QDirListing::IteratorFlags{F::ResolveSymlinks}
<< QStringList("*")
<< QStringList{
"entrylist/file"_L1,
"entrylist/directory"_L1,
"entrylist/writable"_L1,
} + nonBrokenSymlinks;
QTest::newRow("Recursive-ResolveSymlinks")
<< QString("entrylist")
<< QDirListing::IteratorFlags(F::Recursive | F::ResolveSymlinks)
<< QStringList("*")
<< QStringList{
"entrylist/file"_L1,
"entrylist/directory"_L1,
"entrylist/directory/dummy"_L1,
"entrylist/writable"_L1,
} + nonBrokenSymlinks;
QTest::newRow("Recursive-FilesOnly")
<< QString("entrylist")
<< QDirListing::IteratorFlags(F::Recursive | F::FilesOnly)
<< QStringList("*")
<< QStringList{
"entrylist/directory/dummy"_L1,
"entrylist/file"_L1,
"entrylist/writable"_L1,
};
QTest::newRow("Recursive-FilesOnly-ResolveSymlinks")
<< QString("entrylist")
<< QDirListing::IteratorFlags(F::Recursive | F::FilesOnly | F::ResolveSymlinks)
<< QStringList("*")
<< QStringList{
"entrylist/file"_L1,
"entrylist/directory/dummy"_L1,
"entrylist/writable"_L1,
#if !defined(Q_NO_SYMLINKS)
"entrylist/linktofile.lnk"_L1,
#endif #endif
"entrylist/writable").split(','); };
QTest::newRow("QDir::Subdirectories | QDir::FollowSymlinks / QDir::Files") QTest::newRow("Recursive-DirsOnly")
<< QString("entrylist") << QDirListing::IteratorFlags(ItFlag::Recursive | QDirListing::IteratorFlag::FollowSymlinks) << QString("entrylist")
<< QDir::Filters(QDir::Files) << QStringList("*") << QDirListing::IteratorFlags(F::Recursive | F::DirsOnly)
<< QString("entrylist/file," << QStringList("*")
#ifndef Q_NO_SYMLINKS << QStringList{ "entrylist/directory"_L1, };
"entrylist/linktofile.lnk,"
QTest::newRow("Recursive-DirsOnly-ResolveSymlinks")
<< QString("entrylist")
<< QDirListing::IteratorFlags(F::Recursive | F::DirsOnly | F::ResolveSymlinks)
<< QStringList("*")
<< QStringList{
"entrylist/directory"_L1,
#if !defined(Q_NO_SYMLINKS)
"entrylist/linktodirectory.lnk"_L1,
#endif #endif
"entrylist/directory/dummy," };
"entrylist/writable").split(',');
QTest::newRow("empty, default") QTest::newRow("FollowDirSymlinks")
<< QString("empty") << QDirListing::IteratorFlags{} << QString("entrylist")
<< QDir::Filters(QDir::NoFilter) << QStringList("*") << QDirListing::IteratorFlags{F::FollowDirSymlinks}
<< QString("empty/.,empty/..").split(','); << QStringList("*")
<< QStringList{
"entrylist/file"_L1,
"entrylist/directory"_L1,
"entrylist/writable"_L1,
} + allSymlinks;
QTest::newRow("empty, QDir::NoDotAndDotDot") QTest::newRow("FollowDirSymlinks-Recursive")
<< QString("entrylist")
<< QDirListing::IteratorFlags{F::FollowDirSymlinks | F::Recursive}
<< QStringList("*")
<< QStringList{
"entrylist/file"_L1,
"entrylist/directory"_L1,
"entrylist/directory/dummy"_L1,
"entrylist/writable"_L1,
} + allSymlinks;
QTest::newRow("FollowDirSymlinks-Recursive-ResolveSymlinks")
<< QString("entrylist")
<< QDirListing::IteratorFlags{F::FollowDirSymlinks | F::Recursive | F::ResolveSymlinks}
<< QStringList("*")
<< QStringList{
"entrylist/file"_L1,
"entrylist/directory"_L1,
"entrylist/directory/dummy"_L1,
"entrylist/writable"_L1,
} + nonBrokenSymlinks;
QTest::newRow("empty-dir-IncludeDotAndDotDot")
<< QString("empty")
<< QDirListing::IteratorFlags{F::IncludeDotAndDotDot}
<< QStringList("*")
<< QStringList{
"empty/."_L1,
"empty/.."_L1
};
QTest::newRow("empty-dir-Default-Flag")
<< QString("empty") << QDirListing::IteratorFlags{} << QString("empty") << QDirListing::IteratorFlags{}
<< QDir::Filters(QDir::NoDotAndDotDot) << QStringList("*") << QStringList("*")
<< QStringList(); << QStringList();
QTest::newRow("Recursive-nameFilter")
<< u"entrylist"_s
<< QDirListing::IteratorFlags(F::Recursive)
<< QStringList{"dummy"_L1}
<< QStringList{"entrylist/directory/dummy"_L1};
} }
void tst_QDirListing::iterateRelativeDirectory() void tst_QDirListing::iterateRelativeDirectory()
{ {
QFETCH(QString, dirName); QFETCH(QString, dirName);
QFETCH(QDirListing::IteratorFlags, flags); QFETCH(QDirListing::IteratorFlags, flags);
QFETCH(QDir::Filters, filters);
QFETCH(QStringList, nameFilters); QFETCH(QStringList, nameFilters);
QFETCH(const QStringList, entries); QFETCH(const QStringList, entries);
// If canonicalFilePath is empty (e.g. for broken symlinks), use absoluteFilePath()
QStringList list; QStringList list;
for (const auto &dirEntry : QDirListing(dirName, nameFilters, filters, flags)) { for (const auto &dirEntry : QDirListing(dirName, nameFilters, flags)) {
// Using canonical file paths for final comparison QString filePath = dirEntry.canonicalFilePath();
list << dirEntry.fileInfo().canonicalFilePath(); list.emplace_back(!filePath.isEmpty()? filePath : dirEntry.absoluteFilePath());
} }
// The order of items returned by QDirListing is not guaranteed. // The order of items returned by QDirListing is not guaranteed.
list.sort(); list.sort();
QStringList sortedEntries; QStringList sortedEntries;
for (const QString &item : entries) for (const QString &item : entries) {
sortedEntries.append(QFileInfo(item).canonicalFilePath()); QFileInfo fi(item);
QString filePath = fi.canonicalFilePath();
sortedEntries.emplace_back(!filePath.isEmpty()? filePath : fi.absoluteFilePath());
}
sortedEntries.sort(); sortedEntries.sort();
if (sortedEntries != list) { QCOMPARE_EQ(list, sortedEntries);
qDebug() << "ACTUAL: " << list;
qDebug() << "EXPECTED:" << sortedEntries;
}
QCOMPARE(list, sortedEntries);
} }
void tst_QDirListing::iterateResource_data() void tst_QDirListing::iterateResource_data()
{ {
QTest::addColumn<QString>("dirName"); // relative from current path or abs QTest::addColumn<QString>("dirName"); // relative from current path or abs
QTest::addColumn<QDirListing::IteratorFlags>("flags"); QTest::addColumn<QDirListing::IteratorFlags>("flags");
QTest::addColumn<QDir::Filters>("filters");
QTest::addColumn<QStringList>("nameFilters"); QTest::addColumn<QStringList>("nameFilters");
QTest::addColumn<QStringList>("entries"); QTest::addColumn<QStringList>("entries");
QTest::newRow("invalid") << QString::fromLatin1(":/testdata/burpaburpa") << QDirListing::IteratorFlags{} QTest::newRow("invalid") << QString::fromLatin1(":/testdata/burpaburpa")
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*")) << QDirListing::IteratorFlags{ItFlag::Default}
<< QStringList(); << QStringList{u"*"_s} << QStringList();
QTest::newRow("qrc:/testdata") << u":/testdata/"_s << QDirListing::IteratorFlags{} QTest::newRow("qrc:/testdata") << u":/testdata/"_s << QDirListing::IteratorFlags{}
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*")) << QStringList(QLatin1String("*"))
<< QString::fromLatin1(":/testdata/entrylist").split(QLatin1String(",")); << QStringList{u":/testdata/entrylist"_s};
QTest::newRow("qrc:/testdata/entrylist") << u":/testdata/entrylist"_s << QDirListing::IteratorFlags{}
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*")) QTest::newRow("qrc:/testdata/entrylist")
<< QString::fromLatin1(":/testdata/entrylist/directory,:/testdata/entrylist/file").split(QLatin1String(",")); << u":/testdata/entrylist"_s
QTest::newRow("qrc:/testdata recursive") << u":/testdata"_s << QDirListing::IteratorFlags{}
<< QDirListing::IteratorFlags(ItFlag::Recursive) << QStringList(QLatin1String("*"))
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*")) << QStringList{u":/testdata/entrylist/directory"_s,
<< QString::fromLatin1(":/testdata/entrylist,:/testdata/entrylist/directory,:/testdata/entrylist/directory/dummy,:/testdata/entrylist/file").split(QLatin1String(",")); u":/testdata/entrylist/file"_s};
QTest::newRow("qrc:/testdata recursive")
<< u":/testdata"_s
<< QDirListing::IteratorFlags(ItFlag::Recursive)
<< QStringList(QLatin1String("*"))
<< QStringList{u":/testdata/entrylist"_s,
u":/testdata/entrylist/directory"_s,
u":/testdata/entrylist/directory/dummy"_s,
u":/testdata/entrylist/file"_s};
} }
void tst_QDirListing::iterateResource() void tst_QDirListing::iterateResource()
{ {
QFETCH(QString, dirName); QFETCH(QString, dirName);
QFETCH(QDirListing::IteratorFlags, flags); QFETCH(QDirListing::IteratorFlags, flags);
QFETCH(QDir::Filters, filters);
QFETCH(QStringList, nameFilters); QFETCH(QStringList, nameFilters);
QFETCH(QStringList, entries); QFETCH(QStringList, entries);
QStringList list; QStringList list;
for (const auto &dirEntry : QDirListing(dirName, nameFilters, filters, flags)) { for (const auto &dirEntry : QDirListing(dirName, nameFilters, flags)) {
QString dir = dirEntry.fileInfo().filePath(); QString dir = dirEntry.fileInfo().filePath();
if (!dir.startsWith(":/qt-project.org")) if (!dir.startsWith(":/qt-project.org"))
list.emplace_back(std::move(dir)); list.emplace_back(std::move(dir));
@ -404,7 +479,7 @@ void tst_QDirListing::stopLinkLoop()
createLink("..", "entrylist/directory/entrylist4.lnk"); createLink("..", "entrylist/directory/entrylist4.lnk");
#endif #endif
constexpr auto flags = ItFlag::Recursive | ItFlag::FollowSymlinks; constexpr auto flags = ItFlag::Recursive | ItFlag::FollowDirSymlinks;
QDirListing dirIter(u"entrylist"_s, flags); QDirListing dirIter(u"entrylist"_s, flags);
QStringList list; QStringList list;
int max = 200; int max = 200;
@ -424,7 +499,8 @@ public:
: QFSFileEngine(fileName) : QFSFileEngine(fileName)
{ } { }
IteratorUniquePtr beginEntryList(const QString &, QDir::Filters, const QStringList &) override IteratorUniquePtr beginEntryList(const QString &, QDirListing::IteratorFlags,
const QStringList &) override
{ return nullptr; } { return nullptr; }
}; };
@ -468,7 +544,6 @@ void tst_QDirListing::testQFsFileEngineIterator()
{ {
QFETCH(QString, dirName); QFETCH(QString, dirName);
QFETCH(QStringList, nameFilters); QFETCH(QStringList, nameFilters);
QFETCH(QDir::Filters, filters);
QFETCH(QDirListing::IteratorFlags, flags); QFETCH(QDirListing::IteratorFlags, flags);
if (dirName == u"empty") if (dirName == u"empty")
@ -476,7 +551,7 @@ void tst_QDirListing::testQFsFileEngineIterator()
CustomEngineHandler handler; CustomEngineHandler handler;
bool isEmpty = true; bool isEmpty = true;
for (const auto &dirEntry : QDirListing(u"entrylist"_s, nameFilters, filters, flags)) { for (const auto &dirEntry : QDirListing(u"entrylist"_s, nameFilters, flags)) {
if (dirEntry.filePath().contains(u"entrylist")) if (dirEntry.filePath().contains(u"entrylist"))
isEmpty = false; // At least one entry in `entrylist` dir isEmpty = false; // At least one entry in `entrylist` dir
} }
@ -486,7 +561,7 @@ void tst_QDirListing::testQFsFileEngineIterator()
void tst_QDirListing::absoluteFilePathsFromRelativeIteratorPath() void tst_QDirListing::absoluteFilePathsFromRelativeIteratorPath()
{ {
for (const auto &dirEntry : QDirListing(u"entrylist/"_s, QDir::NoDotAndDotDot)) for (const auto &dirEntry : QDirListing(u"entrylist/"_s, QDirListing::IteratorFlag::Recursive))
QVERIFY(dirEntry.absoluteFilePath().contains("entrylist")); QVERIFY(dirEntry.absoluteFilePath().contains("entrylist"));
} }
@ -497,12 +572,11 @@ void tst_QDirListing::recurseWithFilters() const
expectedEntries.insert(QString::fromLatin1("recursiveDirs/dir1/textFileB.txt")); expectedEntries.insert(QString::fromLatin1("recursiveDirs/dir1/textFileB.txt"));
expectedEntries.insert(QString::fromLatin1("recursiveDirs/textFileA.txt")); expectedEntries.insert(QString::fromLatin1("recursiveDirs/textFileA.txt"));
for (const auto &dirEntry : QDirListing(u"recursiveDirs/"_s, QStringList{u"*.txt"_s}, constexpr auto flags = ItFlag::ExcludeDirs | ItFlag::ExcludeSpecial| ItFlag::Recursive;
QDir::Files, ItFlag::Recursive)) { for (const auto &dirEntry : QDirListing(u"recursiveDirs/"_s, QStringList{u"*.txt"_s}, flags))
actualEntries.insert(dirEntry.filePath()); actualEntries.insert(dirEntry.filePath());
}
QCOMPARE(actualEntries, expectedEntries); QCOMPARE_EQ(actualEntries, expectedEntries);
} }
void tst_QDirListing::longPath() void tst_QDirListing::longPath()
@ -518,7 +592,8 @@ void tst_QDirListing::longPath()
dirName.append('x'); dirName.append('x');
} }
QDirListing dirList(dir.absolutePath(), QDir::NoDotAndDotDot|QDir::Dirs, ItFlag::Recursive); constexpr auto flags = ItFlag::ExcludeFiles | ItFlag::ExcludeSpecial| ItFlag::Recursive;
QDirListing dirList(dir.absolutePath(), flags);
qsizetype m = 0; qsizetype m = 0;
for (auto it = dirList.begin(); it != dirList.end(); ++it) for (auto it = dirList.begin(); it != dirList.end(); ++it)
++m; ++m;
@ -562,8 +637,7 @@ void tst_QDirListing::uncPaths_data()
void tst_QDirListing::uncPaths() void tst_QDirListing::uncPaths()
{ {
QFETCH(QString, dirName); QFETCH(QString, dirName);
constexpr auto dirFilters = QDir::AllEntries | QDir::NoDotAndDotDot; for (const auto &dirEntry : QDirListing(dirName, ItFlag::Recursive)) {
for (const auto &dirEntry : QDirListing(dirName, dirFilters, ItFlag::Recursive)) {
const QString &filePath = dirEntry.filePath(); const QString &filePath = dirEntry.filePath();
QCOMPARE(filePath, QDir::cleanPath(filePath)); QCOMPARE(filePath, QDir::cleanPath(filePath));
} }
@ -588,15 +662,13 @@ void tst_QDirListing::hiddenFiles()
}; };
expected.sort(); expected.sort();
constexpr auto filters = QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot; constexpr auto flags = ItFlag::ExcludeDirs | ItFlag::IncludeHidden | ItFlag::Recursive;
QStringList list; QStringList list;
list.reserve(expected.size()); list.reserve(expected.size());
for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, filters, for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, flags)) {
ItFlag::Recursive)) {
QVERIFY(dirEntry.isFile()); QVERIFY(dirEntry.isFile());
list.emplace_back(dirEntry.filePath()); list.emplace_back(dirEntry.filePath());
} }
list.sort(); list.sort();
QCOMPARE_EQ(list, expected); QCOMPARE_EQ(list, expected);
@ -614,11 +686,10 @@ void tst_QDirListing::hiddenDirs()
}; };
expected.sort(); expected.sort();
constexpr auto filters = QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot; constexpr auto flags = ItFlag::ExcludeFiles | ItFlag::IncludeHidden | ItFlag::Recursive;
QStringList list; QStringList list;
list.reserve(expected.size()); list.reserve(expected.size());
for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, filters, for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, flags)) {
ItFlag::Recursive)) {
QVERIFY(dirEntry.isDir()); QVERIFY(dirEntry.isDir());
list.emplace_back(dirEntry.filePath()); list.emplace_back(dirEntry.filePath());
} }
@ -631,7 +702,7 @@ void tst_QDirListing::hiddenDirs()
void tst_QDirListing::withStdAlgorithms() void tst_QDirListing::withStdAlgorithms()
{ {
QDirListing dirList(u"entrylist"_s, QDir::AllEntries | QDir::NoDotAndDotDot, ItFlag::Recursive); QDirListing dirList(u"entrylist"_s, ItFlag::Recursive);
std::for_each(dirList.cbegin(), dirList.cend(), [](const auto &dirEntry) { std::for_each(dirList.cbegin(), dirList.cend(), [](const auto &dirEntry) {
QVERIFY(dirEntry.absoluteFilePath().contains("entrylist")); QVERIFY(dirEntry.absoluteFilePath().contains("entrylist"));

View File

@ -2327,7 +2327,8 @@ public:
MyEngine(int n) { number = n; } MyEngine(int n) { number = n; }
qint64 size() const override { return 123 + number; } qint64 size() const override { return 123 + number; }
QStringList entryList(QDir::Filters, const QStringList &) const override { return QStringList(); } QStringList entryList(QDirListing::IteratorFlags, const QStringList &) const override
{ return QStringList(); }
QString fileName(FileName) const override { return name; } QString fileName(FileName) const override { return name; }
private: private:

View File

@ -206,12 +206,14 @@ void tst_QDirIterator::dirlisting()
{ {
QFETCH(QByteArray, dirpath); QFETCH(QByteArray, dirpath);
using F = QDirListing::IteratorFlag;
int count = 0; int count = 0;
QBENCHMARK { QBENCHMARK {
int c = 0; int c = 0;
QDirListing dir(dirpath, dirFilters, QDirListing::IteratorFlag::Recursive); QDirListing dir(dirpath, F::Recursive | F::IncludeHidden);
for (const auto &dirEntry : dir) { for (const auto &dirEntry : dir) {
const auto path = dirEntry.filePath(); const auto path = dirEntry.filePath();