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:
parent
c610cfe328
commit
94dfcaac8a
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user