QDirIterator: port to QDirListing internally

De-duplicates the code between the two classes.

[ChangeLog][Core][QDirIterator] This class has been deprecated and may
be removed in a future Qt release. Use QDirListing instead.

Change-Id: Iceba5091e72ea462dc9c5145a474bf312ee78043
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Ahmad Samir 2024-02-17 22:36:40 +02:00
parent c610cfe328
commit 94dfcaac8a

View File

@ -34,6 +34,9 @@
you cannot iterate directories in reverse order) and does not allow random
access.
\note This class is deprecated and may be removed in a Qt release. Use
QDirListing instead.
\sa QDir, QDir::entryList()
*/
@ -56,6 +59,8 @@
#include "qdiriterator.h"
#include "qdir_p.h"
#include "qabstractfileengine_p.h"
#include "qdirlisting.h"
#include "qdirentryinfo_p.h"
#include <QtCore/qset.h>
#include <QtCore/qstack.h>
@ -81,292 +86,66 @@ using namespace Qt::StringLiterals;
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:
void init(bool resolveEngine);
void advance();
QDirIteratorPrivate(const QDir &dir, QDirIterator::IteratorFlags flags)
: lister(dir, toDirListingFlags(flags))
{
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();
}
bool entryMatches(const QString & fileName, const QFileInfo &fileInfo);
void pushDirectory(const QFileInfo &fileInfo);
void checkAndPushDirectory(const QFileInfo &);
bool matchesFilters(const QString &fileName, const QFileInfo &fi) const;
void init()
{
it = lister.begin();
if (it != lister.end())
nextFileInfo = it->fileInfo();
}
std::unique_ptr<QAbstractFileEngine> engine;
QFileSystemEntry dirEntry;
QStringList nameFilters;
QDir::Filters filters;
QDirIterator::IteratorFlags iteratorFlags;
#if QT_CONFIG(regularexpression)
QList<QRegularExpression> nameRegExps;
#endif
using FEngineIteratorPtr = std::unique_ptr<QAbstractFileEngineIterator>;
std::stack<FEngineIteratorPtr, std::vector<FEngineIteratorPtr>> fileEngineIterators;
#ifndef QT_NO_FILESYSTEMITERATOR
using FsIteratorPtr = std::unique_ptr<QFileSystemIterator>;
std::stack<FsIteratorPtr, std::vector<FsIteratorPtr>> nativeIterators;
#endif
void advance()
{
currentFileInfo = nextFileInfo;
if (++it != lister.end()) {
nextFileInfo = it->fileInfo();
}
}
QDirListing lister;
QDirListing::const_iterator it = {};
QFileInfo currentFileInfo;
QFileInfo nextFileInfo;
// Loop protection
QDuplicateTracker<QString> visitedLinks;
};
void QDirIteratorPrivate::init(bool resolveEngine = true)
{
if (nameFilters.contains("*"_L1))
nameFilters.clear();
if (filters == QDir::NoFilter)
filters = QDir::AllEntries;
#if QT_CONFIG(regularexpression)
nameRegExps.reserve(nameFilters.size());
for (const auto &filter : nameFilters) {
auto re = QRegularExpression::fromWildcard(filter, (filters & QDir::CaseSensitive ?
Qt::CaseSensitive : Qt::CaseInsensitive));
nameRegExps.append(re);
}
#endif
QFileSystemMetaData metaData;
if (resolveEngine)
engine.reset(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(dirEntry, metaData));
QFileInfo fileInfo(new QFileInfoPrivate(dirEntry, metaData));
// Populate fields for hasNext() and next()
pushDirectory(fileInfo);
advance();
}
/*!
\internal
*/
void QDirIteratorPrivate::pushDirectory(const QFileInfo &fileInfo)
{
QString path = fileInfo.filePath();
#ifdef Q_OS_WIN
if (fileInfo.isSymLink())
path = fileInfo.canonicalFilePath();
#endif
if ((iteratorFlags & QDirIterator::FollowSymlinks)) {
// Stop link loops
if (visitedLinks.hasSeen(fileInfo.canonicalFilePath()))
return;
}
if (engine) {
engine->setFileName(path);
QAbstractFileEngineIterator *it = engine->beginEntryList(filters, nameFilters);
if (it) {
it->setPath(path);
fileEngineIterators.emplace(FEngineIteratorPtr(it));
} else {
// No iterator; no entry list.
}
} else {
#ifndef QT_NO_FILESYSTEMITERATOR
nativeIterators.emplace(std::make_unique<QFileSystemIterator>(
fileInfo.d_ptr->fileEntry, filters));
#else
qWarning("Qt was built with -no-feature-filesystemiterator: no files/plugins will be found!");
#endif
}
}
inline bool QDirIteratorPrivate::entryMatches(const QString & fileName, const QFileInfo &fileInfo)
{
checkAndPushDirectory(fileInfo);
if (matchesFilters(fileName, fileInfo)) {
currentFileInfo = nextFileInfo;
nextFileInfo = fileInfo;
//We found a matching entry.
return true;
}
return false;
}
/*!
\internal
Advances the internal iterator, either a QAbstractFileEngineIterator (e.g.
QResourceFileEngineIterator) or a QFileSystemIterator (which uses low-level
system methods, e.g. readdir() on Unix).
An iterator stack is used for holding the iterators.
A typical example of doing recursive iteration:
- while iterating directory A we find a sub-dir B
- an iterator for B is added to the iterator stack
- B's iterator is processed (the top() of the stack) first; then loop
goes back to processing A's iterator
*/
void QDirIteratorPrivate::advance()
{
// Use get() in both code paths below because the iterator returned by top()
// may be invalidated due to reallocation when appending new iterators in
// pushDirectory().
if (engine) {
while (!fileEngineIterators.empty()) {
// Find the next valid iterator that matches the filters.
QAbstractFileEngineIterator *it;
while (it = fileEngineIterators.top().get(), it->hasNext()) {
it->next();
if (entryMatches(it->currentFileName(), it->currentFileInfo()))
return;
}
fileEngineIterators.pop();
}
} else {
#ifndef QT_NO_FILESYSTEMITERATOR
QFileSystemEntry nextEntry;
QFileSystemMetaData nextMetaData;
while (!nativeIterators.empty()) {
// Find the next valid iterator that matches the filters.
QFileSystemIterator *it;
while (it = nativeIterators.top().get(), it->advance(nextEntry, nextMetaData)) {
QFileInfo info(new QFileInfoPrivate(nextEntry, nextMetaData));
if (entryMatches(nextEntry.fileName(), info))
return;
nextMetaData = QFileSystemMetaData();
}
nativeIterators.pop();
}
#endif
}
currentFileInfo = nextFileInfo;
nextFileInfo = QFileInfo();
}
/*!
\internal
*/
void QDirIteratorPrivate::checkAndPushDirectory(const QFileInfo &fileInfo)
{
// If we're doing flat iteration, we're done.
if (!(iteratorFlags & QDirIterator::Subdirectories))
return;
// Never follow non-directory entries
if (!fileInfo.isDir())
return;
// Follow symlinks only when asked
if (!(iteratorFlags & QDirIterator::FollowSymlinks) && fileInfo.isSymLink())
return;
// Never follow . and ..
QString fileName = fileInfo.fileName();
if ("."_L1 == fileName || ".."_L1 == fileName)
return;
// No hidden directories unless requested
if (!(filters & QDir::AllDirs) && !(filters & QDir::Hidden) && fileInfo.isHidden())
return;
pushDirectory(fileInfo);
}
/*!
\internal
This convenience function implements the iterator's filtering logics and
applies then to the current directory entry.
It 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, false is returned.
*/
bool QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInfo &fi) const
{
if (fileName.isEmpty())
return false;
// filter . and ..?
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)
return false;
if ((filters & QDir::NoDotDot) && dotOrDotDot && fileNameSize == 2)
return false;
// name filter
#if QT_CONFIG(regularexpression)
// Pass all entries through name filters, except dirs if the AllDirs
if (!nameFilters.isEmpty() && !((filters & QDir::AllDirs) && fi.isDir())) {
bool matched = false;
for (const auto &re : nameRegExps) {
if (re.match(fileName).hasMatch()) {
matched = true;
break;
}
}
if (!matched)
return false;
}
#endif
// skip symlinks
const bool skipSymlinks = filters.testAnyFlag(QDir::NoSymLinks);
const bool includeSystem = filters.testAnyFlag(QDir::System);
if (skipSymlinks && fi.isSymLink()) {
// The only reason to save this file is if it is a broken link and we are requesting system files.
if (!includeSystem || fi.exists())
return false;
}
// filter hidden
const bool includeHidden = filters.testAnyFlag(QDir::Hidden);
if (!includeHidden && !dotOrDotDot && fi.isHidden())
return false;
// filter system files
if (!includeSystem && (!(fi.isFile() || fi.isDir() || fi.isSymLink())
|| (!fi.exists() && fi.isSymLink())))
return false;
// skip directories
const bool skipDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
if (skipDirs && fi.isDir())
return false;
// skip files
const bool skipFiles = !(filters & QDir::Files);
if (skipFiles && fi.isFile())
// Basically we need a reason not to exclude this file otherwise we just eliminate it.
return false;
// filter permissions
const bool filterPermissions = ((filters & QDir::PermissionMask)
&& (filters & QDir::PermissionMask) != QDir::PermissionMask);
const bool doWritable = !filterPermissions || (filters & QDir::Writable);
const bool doExecutable = !filterPermissions || (filters & QDir::Executable);
const bool doReadable = !filterPermissions || (filters & QDir::Readable);
if (filterPermissions
&& ((doReadable && !fi.isReadable())
|| (doWritable && !fi.isWritable())
|| (doExecutable && !fi.isExecutable()))) {
return false;
}
return true;
}
/*!
Constructs a QDirIterator that can iterate over \a dir's entrylist, using
\a dir's name filters and regular filters. You can pass options via \a
@ -383,15 +162,8 @@ bool QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInf
\sa hasNext(), next(), IteratorFlags
*/
QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
: d(new QDirIteratorPrivate)
: d(new QDirIteratorPrivate(dir, flags))
{
const QDirPrivate *other = dir.d_ptr.constData();
d->dirEntry = other->dirEntry;
d->nameFilters = other->nameFilters;
d->filters = other->filters;
d->iteratorFlags = flags;
const bool resolveEngine = other->fileEngine ? true : false;
d->init(resolveEngine);
}
/*!
@ -408,12 +180,8 @@ QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
\sa hasNext(), next(), IteratorFlags
*/
QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags)
: d(new QDirIteratorPrivate)
: d(new QDirIteratorPrivate(path, filters, flags))
{
d->dirEntry = QFileSystemEntry(path);
d->filters = filters;
d->iteratorFlags = flags;
d->init();
}
/*!
@ -429,12 +197,8 @@ QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorF
\sa hasNext(), next(), IteratorFlags
*/
QDirIterator::QDirIterator(const QString &path, IteratorFlags flags)
: d(new QDirIteratorPrivate)
: d(new QDirIteratorPrivate(path, flags))
{
d->dirEntry = QFileSystemEntry(path);
d->filters = QDir::NoFilter;
d->iteratorFlags = flags;
d->init();
}
/*!
@ -457,13 +221,8 @@ QDirIterator::QDirIterator(const QString &path, IteratorFlags flags)
*/
QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters,
QDir::Filters filters, IteratorFlags flags)
: d(new QDirIteratorPrivate)
: d(new QDirIteratorPrivate(path, nameFilters, filters, flags))
{
d->dirEntry = QFileSystemEntry(path);
d->nameFilters = nameFilters;
d->filters = filters;
d->iteratorFlags = flags;
d->init();
}
/*!
@ -488,7 +247,7 @@ QDirIterator::~QDirIterator()
QString QDirIterator::next()
{
d->advance();
return filePath();
return d->currentFileInfo.filePath();
}
/*!
@ -508,7 +267,7 @@ QString QDirIterator::next()
QFileInfo QDirIterator::nextFileInfo()
{
d->advance();
return fileInfo();
return d->currentFileInfo;
}
/*!
@ -519,14 +278,7 @@ QFileInfo QDirIterator::nextFileInfo()
*/
bool QDirIterator::hasNext() const
{
if (d->engine)
return !d->fileEngineIterators.empty();
else
#ifndef QT_NO_FILESYSTEMITERATOR
return !d->nativeIterators.empty();
#else
return false;
#endif
return d->it != d->lister.end();
}
/*!
@ -569,7 +321,7 @@ QFileInfo QDirIterator::fileInfo() const
*/
QString QDirIterator::path() const
{
return d->dirEntry.filePath();
return d->lister.iteratorPath();
}
QT_END_NAMESPACE