Add QDirListing, an STL-style iterator for directory entries
This class offers a forward-only const_iterator, that matches the system low-level functions' logic (e.g. readdir()/dirstream logic). This iterator is a std::input_iterator_tag. QDirIterator uses Java-style iterators that have a couple of issues: - They don't fit the logic of the underlying native system functions (readdir()/__dirstream and co.), there is no way to know if there is a next entry except by advancing the iterator (calling readdir()) first - As a consequence of the above, two QFileInfo objects, current and next, had to be used to fit that paradigm; and the code always iterated/stat'ed an extra entry past the one we want, e.g. when filtering The next step is porting QAbstractFileEngineIterator and its subclasses to be like QFileSystemIterator, i.e. replace hasNext()/next() with a `bool advance()` virtual method. This is easier to reason about than the Java-style iterators, and is more in-line with the new class. Discussed-on: https://lists.qt-project.org/pipermail/development/2023-December/044745.html Change-Id: I8e696cefdca18d8c78f803efdb83a73dd43eb720 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
78c33a7741
commit
c39a0d1e89
@ -116,6 +116,7 @@ qt_internal_add_module(Core
|
||||
io/qdataurl.cpp io/qdataurl_p.h
|
||||
io/qdebug.cpp io/qdebug.h io/qdebug_p.h
|
||||
io/qdir.cpp io/qdir.h io/qdir_p.h
|
||||
io/qdirlisting.cpp io/qdirlisting.h io/qdirentryinfo_p.h
|
||||
io/qdiriterator.cpp io/qdiriterator.h
|
||||
io/qfile.cpp io/qfile.h io/qfile_p.h
|
||||
io/qfiledevice.cpp io/qfiledevice.h io/qfiledevice_p.h
|
||||
|
@ -1,11 +1,16 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <QDirListing>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
[[maybe_unused]] static void func() {
|
||||
{
|
||||
//! [0]
|
||||
QDirIterator it("/etc", QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QString dir = it.next();
|
||||
qDebug() << dir;
|
||||
using ItFlag = QDirListing::IteratorFlag;
|
||||
for (const auto &dirEntry : QDirListing(u"/etc"_s, ItFlag::Recursive)) {
|
||||
qDebug() << dirEntry.filePath();
|
||||
// /etc/.
|
||||
// /etc/..
|
||||
// /etc/X11
|
||||
@ -13,16 +18,53 @@ while (it.hasNext()) {
|
||||
// ...
|
||||
}
|
||||
//! [0]
|
||||
}
|
||||
|
||||
{
|
||||
//! [1]
|
||||
QDirIterator it("/sys", QStringList() << "scaling_cur_freq", QDir::NoFilter, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QFile f(it.next());
|
||||
using ItFlag = QDirListing::IteratorFlag;
|
||||
QDirListing dirList(u"/sys"_s, QStringList{u"scaling_cur_freq"_s},
|
||||
QDir::NoFilter, ItFlag::Recursive);
|
||||
for (const auto &dirEntry : dirList) {
|
||||
QFile f(dirEntry.filePath());
|
||||
f.open(QIODevice::ReadOnly);
|
||||
qDebug() << f.fileName() << f.readAll().trimmed().toDouble() / 1000 << "MHz";
|
||||
}
|
||||
//! [1]
|
||||
}
|
||||
|
||||
{
|
||||
//! [2]
|
||||
QDirIterator audioFileIt(audioPath, {"*.mp3", "*.wav"}, QDir::Files);
|
||||
QDirListing audioFileIt(u"/home/johndoe/"_s, {"*.mp3", "*.wav"}, QDir::Files);
|
||||
//! [2]
|
||||
}
|
||||
|
||||
{
|
||||
//! [3]
|
||||
using ItFlag = QDirListing::IteratorFlag;
|
||||
for (const auto &dirEntry : QDirListing(u"/etc"_s, ItFlag::Recursive)) {
|
||||
// Faster
|
||||
if (dirEntry.fileName().endsWith(u".conf")) { /* ... */ }
|
||||
|
||||
// This works, but might be potentially slower, since it has to construct a
|
||||
// QFileInfo, whereas (depending on the implemnetation) the fileName could
|
||||
// be known already
|
||||
if (dirEntry.fileInfo().fileName().endsWith(u".conf")) { /* ... */ }
|
||||
}
|
||||
//! [3]
|
||||
}
|
||||
|
||||
{
|
||||
//! [4]
|
||||
using ItFlag = QDirListing::IteratorFlag;
|
||||
for (const auto &dirEntry : QDirListing(u"/etc"_s, ItFlag::Recursive)) {
|
||||
// Both approaches are the same, because DirEntry will have to construct
|
||||
// a QFileInfo to get this info (for example, by calling system stat())
|
||||
|
||||
if (dirEntry.size() >= 4'000 /* 4KB */) { /* ...*/ }
|
||||
if (dirEntry.fileInfo().size() >= 4'000 /* 4KB */) { /* ... */ }
|
||||
}
|
||||
//! [4]
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -218,6 +218,7 @@ private:
|
||||
Q_DISABLE_COPY_MOVE(QAbstractFileEngineIterator)
|
||||
friend class QDirIterator;
|
||||
friend class QDirIteratorPrivate;
|
||||
friend class QDirListingPrivate;
|
||||
void setPath(const QString &path);
|
||||
QScopedPointer<QAbstractFileEngineIteratorPrivate> d;
|
||||
};
|
||||
|
@ -243,6 +243,7 @@ private:
|
||||
friend Q_CORE_EXPORT bool comparesEqual(const QDir &lhs, const QDir &rhs);
|
||||
Q_DECLARE_EQUALITY_COMPARABLE(QDir)
|
||||
friend class QDirIterator;
|
||||
friend class QDirListing;
|
||||
// Q_DECLARE_PRIVATE equivalent for shared data pointers
|
||||
QDirPrivate *d_func();
|
||||
const QDirPrivate *d_func() const { return d_ptr.constData(); }
|
||||
|
156
src/corelib/io/qdirentryinfo_p.h
Normal file
156
src/corelib/io/qdirentryinfo_p.h
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright (C) 2024 Ahmad Samir <a.samirh78@gmail.com>
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QDIRENTRYINFO_P_H
|
||||
#define QDIRENTRYINFO_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtCore/private/qfileinfo_p.h>
|
||||
#include <QtCore/private/qfilesystementry_p.h>
|
||||
#include <QtCore/private/qfilesystemmetadata_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QDirEntryInfo
|
||||
{
|
||||
const QFileSystemMetaData &ensureFilled(QFileSystemMetaData::MetaDataFlags what)
|
||||
{
|
||||
if (!metaData.hasFlags(what))
|
||||
QFileSystemEngine::fillMetaData(entry, metaData, what);
|
||||
return metaData;
|
||||
}
|
||||
|
||||
public:
|
||||
const QFileInfo &fileInfo()
|
||||
{
|
||||
if (!fileInfoOpt) {
|
||||
fileInfoOpt.emplace(new QFileInfoPrivate(entry, metaData));
|
||||
metaData.clear();
|
||||
}
|
||||
return *fileInfoOpt;
|
||||
}
|
||||
|
||||
QString fileName()
|
||||
{ return fileInfoOpt ? fileInfoOpt->fileName() : entry.fileName(); }
|
||||
QString baseName()
|
||||
{ return fileInfoOpt ? fileInfoOpt->baseName() : entry.baseName(); }
|
||||
QString completeBaseName() const
|
||||
{ return fileInfoOpt ? fileInfoOpt->completeBaseName() : entry.completeBaseName(); }
|
||||
QString suffix() const
|
||||
{ return fileInfoOpt ? fileInfoOpt->suffix() : entry.suffix(); }
|
||||
QString completeSuffix() const
|
||||
{ return fileInfoOpt ? fileInfoOpt->completeSuffix() : entry.completeSuffix(); }
|
||||
QString filePath()
|
||||
{ return fileInfoOpt ? fileInfoOpt->filePath() : entry.filePath(); }
|
||||
|
||||
QString bundleName() { return fileInfo().bundleName(); }
|
||||
|
||||
QString canonicalFilePath()
|
||||
{
|
||||
// QFileInfo caches these strings
|
||||
return fileInfo().canonicalFilePath();
|
||||
}
|
||||
|
||||
QString absoluteFilePath() {
|
||||
// QFileInfo caches these strings
|
||||
return fileInfo().absoluteFilePath();
|
||||
}
|
||||
|
||||
QString absolutePath() {
|
||||
// QFileInfo caches these strings
|
||||
return fileInfo().absolutePath();
|
||||
}
|
||||
|
||||
|
||||
bool isDir() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->isDir();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::DirectoryType).isDirectory();
|
||||
}
|
||||
|
||||
bool isFile() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->isFile();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::FileType).isFile();
|
||||
}
|
||||
|
||||
bool isSymLink() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->isSymLink();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::LegacyLinkType).isLegacyLink();
|
||||
}
|
||||
|
||||
bool isSymbolicLink() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->isSymbolicLink();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::LinkType).isLink();
|
||||
}
|
||||
|
||||
bool exists() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->exists();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::ExistsAttribute).exists();
|
||||
}
|
||||
|
||||
bool isHidden() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->isHidden();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::HiddenAttribute).isHidden();
|
||||
}
|
||||
|
||||
bool isReadable() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->isReadable();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::UserReadPermission).isReadable();
|
||||
}
|
||||
|
||||
bool isWritable() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->isWritable();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::UserWritePermission).isWritable();
|
||||
}
|
||||
|
||||
bool isExecutable() {
|
||||
if (fileInfoOpt)
|
||||
return fileInfoOpt->isExecutable();
|
||||
|
||||
return ensureFilled(QFileSystemMetaData::UserExecutePermission).isExecutable();
|
||||
}
|
||||
|
||||
qint64 size() { return fileInfo().size(); }
|
||||
|
||||
QDateTime fileTime(QFile::FileTime type, const QTimeZone &tz)
|
||||
{
|
||||
return fileInfo().fileTime(type, tz);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class QDirListingPrivate;
|
||||
friend class QDirListing;
|
||||
|
||||
QFileSystemEntry entry;
|
||||
QFileSystemMetaData metaData;
|
||||
std::optional<QFileInfo> fileInfoOpt;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QDIRENTRYINFO_P_H
|
@ -1,65 +1,73 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// Copyright (C) 2024 Ahmad Samir <a.samirh78@gmail.com>
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
/*!
|
||||
\since 4.3
|
||||
\class QDirIterator
|
||||
\since 6.8
|
||||
\class QDirListing
|
||||
\inmodule QtCore
|
||||
\brief The QDirIterator class provides an iterator for directory entrylists.
|
||||
\brief The QDirListing class provides an STL-style iterator for directory entries.
|
||||
|
||||
You can use QDirIterator to navigate entries of a directory one at a time.
|
||||
You can use QDirListing to navigate entries of a directory one at a time.
|
||||
It is similar to QDir::entryList() and QDir::entryInfoList(), but because
|
||||
it lists entries one at a time instead of all at once, it scales better
|
||||
and is more suitable for large directories. It also supports listing
|
||||
directory contents recursively, and following symbolic links. Unlike
|
||||
QDir::entryList(), QDirIterator does not support sorting.
|
||||
QDir::entryList(), QDirListing does not support sorting.
|
||||
|
||||
The QDirIterator constructor takes a QDir or a directory as
|
||||
argument. After construction, the iterator is located before the first
|
||||
directory entry. Here's how to iterate over all the entries sequentially:
|
||||
The QDirListing constructor takes a QDir or a directory path as
|
||||
argument. Here's how to iterate over all entries recursively:
|
||||
|
||||
\snippet code/src_corelib_io_qdiriterator.cpp 0
|
||||
\snippet code/src_corelib_io_qdirlisting.cpp 0
|
||||
|
||||
Here's how to find and read all files filtered by name, recursively:
|
||||
|
||||
\snippet code/src_corelib_io_qdiriterator.cpp 1
|
||||
\snippet code/src_corelib_io_qdirlisting.cpp 1
|
||||
|
||||
The next() and nextFileInfo() functions advance the iterator and return
|
||||
the path or the QFileInfo of the next directory entry. You can also call
|
||||
filePath() or fileInfo() to get the current file path or QFileInfo without
|
||||
first advancing the iterator. The fileName() function returns only the
|
||||
name of the file, similar to how QDir::entryList() works.
|
||||
Iterators constructed by QDirListing (QDirListing::const_iterator) are
|
||||
forward-only (you cannot iterate directories in reverse order) and don't
|
||||
allow random access. They can be used in ranged-for loops (or with STL
|
||||
alogrithms that don't require random access iterators). Dereferencing
|
||||
a valid iterator returns a QDirListing::DirEntry object. The (c)end()
|
||||
iterator marks the end of the iteration. Dereferencing the end iterator
|
||||
is undefiend behavior.
|
||||
|
||||
Unlike Qt's container iterators, QDirIterator is uni-directional (i.e.,
|
||||
you cannot iterate directories in reverse order) and does not allow random
|
||||
access.
|
||||
QDirListing::DirEntry offers a subset of QFileInfo's API (for example,
|
||||
fileName(), filePath(), exists()). Internally, DirEntry only constructs
|
||||
a QFileInfo object if needed, that is, if the info hasn't been already
|
||||
fetched by other system functions. You can use DirEntry::fileInfo()
|
||||
to get a QFileInfo. For example:
|
||||
|
||||
\snippet code/src_corelib_io_qdirlisting.cpp 3
|
||||
\snippet code/src_corelib_io_qdirlisting.cpp 4
|
||||
|
||||
\sa QDir, QDir::entryList()
|
||||
*/
|
||||
|
||||
/*! \enum QDirIterator::IteratorFlag
|
||||
/*! \enum QDirListing::IteratorFlag
|
||||
|
||||
This enum describes flags that you can combine to configure the behavior
|
||||
of QDirIterator.
|
||||
This enum class describes flags can be used to configure the behavior of
|
||||
QDirListing. These flags can be bitwise OR'ed together.
|
||||
|
||||
\value NoIteratorFlags The default value, representing no flags. The
|
||||
iterator will return entries for the assigned path.
|
||||
\value NoFlag The default value, representing no flags. The iterator
|
||||
will return entries for the assigned path.
|
||||
|
||||
\value Subdirectories List entries inside all subdirectories as well.
|
||||
\value FollowSymlinks When combined with Recursive, this flag enables
|
||||
iterating through all subdirectories of the assigned path, following
|
||||
all symbolic links. Symbolic link loops (e.g., link => . or link =>
|
||||
..) are automatically detected and ignored.
|
||||
|
||||
\value FollowSymlinks When combined with Subdirectories, this flag
|
||||
enables iterating through all subdirectories of the assigned path,
|
||||
following all symbolic links. Symbolic link loops (e.g., "link" => "." or
|
||||
"link" => "..") are automatically detected and ignored.
|
||||
\value Recursive List entries inside all subdirectories as well.
|
||||
*/
|
||||
|
||||
#include "qdiriterator.h"
|
||||
#include "qdirlisting.h"
|
||||
#include "qdirentryinfo_p.h"
|
||||
|
||||
#include "qdir_p.h"
|
||||
#include "qabstractfileengine_p.h"
|
||||
|
||||
#include <QtCore/qset.h>
|
||||
#include <QtCore/qstack.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
|
||||
#if QT_CONFIG(regularexpression)
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#endif
|
||||
@ -79,23 +87,26 @@ QT_BEGIN_NAMESPACE
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
class QDirIteratorPrivate
|
||||
class QDirListingPrivate
|
||||
{
|
||||
public:
|
||||
void init(bool resolveEngine);
|
||||
void advance();
|
||||
|
||||
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;
|
||||
bool entryMatches(QDirEntryInfo &info);
|
||||
void pushDirectory(QDirEntryInfo &info);
|
||||
void pushInitialDirectory();
|
||||
|
||||
void checkAndPushDirectory(QDirEntryInfo &info);
|
||||
bool matchesFilters(QDirEntryInfo &data) const;
|
||||
bool hasIterators() const;
|
||||
|
||||
std::unique_ptr<QAbstractFileEngine> engine;
|
||||
|
||||
QFileSystemEntry dirEntry;
|
||||
QDirEntryInfo initialEntryInfo;
|
||||
QStringList nameFilters;
|
||||
QDir::Filters filters;
|
||||
QDirIterator::IteratorFlags iteratorFlags;
|
||||
QDirListing::IteratorFlags iteratorFlags;
|
||||
QDirEntryInfo currentEntryInfo;
|
||||
|
||||
#if QT_CONFIG(regularexpression)
|
||||
QList<QRegularExpression> nameRegExps;
|
||||
@ -108,14 +119,11 @@ public:
|
||||
std::stack<FsIteratorPtr, std::vector<FsIteratorPtr>> nativeIterators;
|
||||
#endif
|
||||
|
||||
QFileInfo currentFileInfo;
|
||||
QFileInfo nextFileInfo;
|
||||
|
||||
// Loop protection
|
||||
QDuplicateTracker<QString> visitedLinks;
|
||||
};
|
||||
|
||||
void QDirIteratorPrivate::init(bool resolveEngine = true)
|
||||
void QDirListingPrivate::init(bool resolveEngine = true)
|
||||
{
|
||||
if (nameFilters.contains("*"_L1))
|
||||
nameFilters.clear();
|
||||
@ -131,31 +139,26 @@ void QDirIteratorPrivate::init(bool resolveEngine = true)
|
||||
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();
|
||||
if (resolveEngine)
|
||||
engine.reset(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(
|
||||
initialEntryInfo.entry, initialEntryInfo.metaData));
|
||||
|
||||
pushDirectory(initialEntryInfo);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void QDirIteratorPrivate::pushDirectory(const QFileInfo &fileInfo)
|
||||
void QDirListingPrivate::pushDirectory(QDirEntryInfo &entryInfo)
|
||||
{
|
||||
QString path = fileInfo.filePath();
|
||||
QString path = entryInfo.filePath();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
if (fileInfo.isSymLink())
|
||||
path = fileInfo.canonicalFilePath();
|
||||
if (entryInfo.isSymLink())
|
||||
path = entryInfo.canonicalFilePath();
|
||||
#endif
|
||||
|
||||
if ((iteratorFlags & QDirIterator::FollowSymlinks)) {
|
||||
if (iteratorFlags.testAnyFlags(QDirListing::IteratorFlag::FollowSymlinks)) {
|
||||
// Stop link loops
|
||||
if (visitedLinks.hasSeen(fileInfo.canonicalFilePath()))
|
||||
if (visitedLinks.hasSeen(entryInfo.canonicalFilePath()))
|
||||
return;
|
||||
}
|
||||
|
||||
@ -170,27 +173,22 @@ void QDirIteratorPrivate::pushDirectory(const QFileInfo &fileInfo)
|
||||
}
|
||||
} else {
|
||||
#ifndef QT_NO_FILESYSTEMITERATOR
|
||||
nativeIterators.emplace(std::make_unique<QFileSystemIterator>(
|
||||
fileInfo.d_ptr->fileEntry, filters));
|
||||
QFileSystemEntry *fentry = nullptr;
|
||||
if (entryInfo.fileInfoOpt)
|
||||
fentry = &entryInfo.fileInfoOpt->d_ptr->fileEntry;
|
||||
else
|
||||
fentry = &entryInfo.entry;
|
||||
nativeIterators.emplace(std::make_unique<QFileSystemIterator>(*fentry, 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)
|
||||
bool QDirListingPrivate::entryMatches(QDirEntryInfo &entryInfo)
|
||||
{
|
||||
checkAndPushDirectory(fileInfo);
|
||||
|
||||
if (matchesFilters(fileName, fileInfo)) {
|
||||
currentFileInfo = nextFileInfo;
|
||||
nextFileInfo = fileInfo;
|
||||
|
||||
//We found a matching entry.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
checkAndPushDirectory(entryInfo);
|
||||
return matchesFilters(entryInfo);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -208,7 +206,7 @@ inline bool QDirIteratorPrivate::entryMatches(const QString & fileName, const QF
|
||||
- B's iterator is processed (the top() of the stack) first; then loop
|
||||
goes back to processing A's iterator
|
||||
*/
|
||||
void QDirIteratorPrivate::advance()
|
||||
void QDirListingPrivate::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
|
||||
@ -220,79 +218,73 @@ void QDirIteratorPrivate::advance()
|
||||
QAbstractFileEngineIterator *it;
|
||||
while (it = fileEngineIterators.top().get(), it->hasNext()) {
|
||||
it->next();
|
||||
if (entryMatches(it->currentFileName(), it->currentFileInfo()))
|
||||
QDirEntryInfo entryInfo;
|
||||
entryInfo.fileInfoOpt = it->currentFileInfo();
|
||||
if (entryMatches(entryInfo)) {
|
||||
currentEntryInfo = std::move(entryInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fileEngineIterators.pop();
|
||||
}
|
||||
} else {
|
||||
#ifndef QT_NO_FILESYSTEMITERATOR
|
||||
QFileSystemEntry nextEntry;
|
||||
QFileSystemMetaData nextMetaData;
|
||||
|
||||
QDirEntryInfo entryInfo;
|
||||
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))
|
||||
while (it = nativeIterators.top().get(), it->advance(entryInfo.entry, entryInfo.metaData)) {
|
||||
if (entryMatches(entryInfo)) {
|
||||
currentEntryInfo = std::move(entryInfo);
|
||||
return;
|
||||
nextMetaData = QFileSystemMetaData();
|
||||
}
|
||||
entryInfo = {};
|
||||
}
|
||||
|
||||
nativeIterators.pop();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
currentFileInfo = nextFileInfo;
|
||||
nextFileInfo = QFileInfo();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
void QDirIteratorPrivate::checkAndPushDirectory(const QFileInfo &fileInfo)
|
||||
void QDirListingPrivate::checkAndPushDirectory(QDirEntryInfo &entryInfo)
|
||||
{
|
||||
using F = QDirListing::IteratorFlag;
|
||||
// If we're doing flat iteration, we're done.
|
||||
if (!(iteratorFlags & QDirIterator::Subdirectories))
|
||||
if (!iteratorFlags.testAnyFlags(F::Recursive))
|
||||
return;
|
||||
|
||||
// Never follow non-directory entries
|
||||
if (!fileInfo.isDir())
|
||||
if (!entryInfo.isDir())
|
||||
return;
|
||||
|
||||
// Follow symlinks only when asked
|
||||
if (!(iteratorFlags & QDirIterator::FollowSymlinks) && fileInfo.isSymLink())
|
||||
if (!iteratorFlags.testAnyFlags(F::FollowSymlinks) && entryInfo.isSymLink())
|
||||
return;
|
||||
|
||||
// Never follow . and ..
|
||||
QString fileName = fileInfo.fileName();
|
||||
const QString &fileName = entryInfo.fileName();
|
||||
if ("."_L1 == fileName || ".."_L1 == fileName)
|
||||
return;
|
||||
|
||||
// No hidden directories unless requested
|
||||
if (!(filters & QDir::AllDirs) && !(filters & QDir::Hidden) && fileInfo.isHidden())
|
||||
if (!filters.testAnyFlags(QDir::AllDirs | QDir::Hidden) && entryInfo.isHidden())
|
||||
return;
|
||||
|
||||
pushDirectory(fileInfo);
|
||||
pushDirectory(entryInfo);
|
||||
}
|
||||
|
||||
/*!
|
||||
\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.
|
||||
This functions 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 QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInfo &fi) const
|
||||
bool QDirListingPrivate::matchesFilters(QDirEntryInfo &entryInfo) const
|
||||
{
|
||||
const QString &fileName = entryInfo.fileName();
|
||||
if (fileName.isEmpty())
|
||||
return false;
|
||||
|
||||
@ -309,7 +301,7 @@ bool QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInf
|
||||
// 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())) {
|
||||
if (!nameFilters.isEmpty() && !(filters.testAnyFlags(QDir::AllDirs) && entryInfo.isDir())) {
|
||||
bool matched = false;
|
||||
for (const auto &re : nameRegExps) {
|
||||
if (re.match(fileName).hasMatch()) {
|
||||
@ -324,30 +316,30 @@ bool QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInf
|
||||
// skip symlinks
|
||||
const bool skipSymlinks = filters.testAnyFlag(QDir::NoSymLinks);
|
||||
const bool includeSystem = filters.testAnyFlag(QDir::System);
|
||||
if (skipSymlinks && fi.isSymLink()) {
|
||||
if (skipSymlinks && entryInfo.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())
|
||||
if (!includeSystem || entryInfo.exists())
|
||||
return false;
|
||||
}
|
||||
|
||||
// filter hidden
|
||||
const bool includeHidden = filters.testAnyFlag(QDir::Hidden);
|
||||
if (!includeHidden && !dotOrDotDot && fi.isHidden())
|
||||
if (!includeHidden && !dotOrDotDot && entryInfo.isHidden())
|
||||
return false;
|
||||
|
||||
// filter system files
|
||||
if (!includeSystem && (!(fi.isFile() || fi.isDir() || fi.isSymLink())
|
||||
|| (!fi.exists() && fi.isSymLink())))
|
||||
if (!includeSystem && (!(entryInfo.isFile() || entryInfo.isDir() || entryInfo.isSymLink())
|
||||
|| (!entryInfo.exists() && entryInfo.isSymLink())))
|
||||
return false;
|
||||
|
||||
// skip directories
|
||||
const bool skipDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
|
||||
if (skipDirs && fi.isDir())
|
||||
if (skipDirs && entryInfo.isDir())
|
||||
return false;
|
||||
|
||||
// skip files
|
||||
const bool skipFiles = !(filters & QDir::Files);
|
||||
if (skipFiles && fi.isFile())
|
||||
if (skipFiles && entryInfo.isFile())
|
||||
// Basically we need a reason not to exclude this file otherwise we just eliminate it.
|
||||
return false;
|
||||
|
||||
@ -358,35 +350,47 @@ bool QDirIteratorPrivate::matchesFilters(const QString &fileName, const QFileInf
|
||||
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()))) {
|
||||
&& ((doReadable && !entryInfo.isReadable())
|
||||
|| (doWritable && !entryInfo.isWritable())
|
||||
|| (doExecutable && !entryInfo.isExecutable()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QDirListingPrivate::hasIterators() const
|
||||
{
|
||||
if (engine)
|
||||
return !fileEngineIterators.empty();
|
||||
|
||||
#if !defined(QT_NO_FILESYSTEMITERATOR)
|
||||
return !nativeIterators.empty();
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
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
|
||||
flags to decide how the directory should be iterated.
|
||||
Constructs a QDirListing that can iterate over \a dir's entries, using
|
||||
\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
|
||||
as in QDir::entryList().
|
||||
|
||||
The sorting in \a dir is ignored.
|
||||
|
||||
\note To list symlinks that point to non existing files, QDir::System must be
|
||||
passed to the flags.
|
||||
\note To list symlinks that point to non existing files, QDir::System
|
||||
must be set in \a dir's QDir::Filters.
|
||||
|
||||
\sa hasNext(), next(), IteratorFlags
|
||||
*/
|
||||
QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
|
||||
: d(new QDirIteratorPrivate)
|
||||
QDirListing::QDirListing(const QDir &dir, IteratorFlags flags)
|
||||
: d(new QDirListingPrivate)
|
||||
{
|
||||
const QDirPrivate *other = dir.d_ptr.constData();
|
||||
d->dirEntry = other->dirEntry;
|
||||
d->initialEntryInfo.entry = other->dirEntry;
|
||||
d->nameFilters = other->nameFilters;
|
||||
d->filters = other->filters;
|
||||
d->iteratorFlags = flags;
|
||||
@ -395,50 +399,47 @@ QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a QDirIterator that can iterate over \a path, with no name
|
||||
filtering and \a filters for entry filtering. You can pass options via \a
|
||||
flags to decide how the directory should be iterated.
|
||||
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
|
||||
passed to the flags.
|
||||
\note To list symlinks that point to non existing files, QDir::System
|
||||
must be set in \a filters.
|
||||
|
||||
\sa hasNext(), next(), IteratorFlags
|
||||
*/
|
||||
QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags)
|
||||
: d(new QDirIteratorPrivate)
|
||||
QDirListing::QDirListing(const QString &path, QDir::Filters filters, IteratorFlags flags)
|
||||
: d(new QDirListingPrivate)
|
||||
{
|
||||
d->dirEntry = QFileSystemEntry(path);
|
||||
d->initialEntryInfo.entry = QFileSystemEntry(path);
|
||||
d->filters = filters;
|
||||
d->iteratorFlags = flags;
|
||||
d->init();
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a QDirIterator that can iterate over \a path. You can pass
|
||||
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().
|
||||
|
||||
\note To list symlinks that point to non existing files, QDir::System must be
|
||||
passed to the flags.
|
||||
|
||||
\sa hasNext(), next(), IteratorFlags
|
||||
*/
|
||||
QDirIterator::QDirIterator(const QString &path, IteratorFlags flags)
|
||||
: d(new QDirIteratorPrivate)
|
||||
QDirListing::QDirListing(const QString &path, IteratorFlags flags)
|
||||
: d(new QDirListingPrivate)
|
||||
{
|
||||
d->dirEntry = QFileSystemEntry(path);
|
||||
d->initialEntryInfo.entry = QFileSystemEntry(path);
|
||||
d->filters = QDir::NoFilter;
|
||||
d->iteratorFlags = flags;
|
||||
d->init();
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a QDirIterator that can iterate over \a path, using \a
|
||||
Constructs a QDirListing that can iterate over \a path, using \a
|
||||
nameFilters and \a filters. You can pass options via \a flags to decide
|
||||
how the directory should be iterated.
|
||||
|
||||
@ -448,18 +449,18 @@ QDirIterator::QDirIterator(const QString &path, IteratorFlags flags)
|
||||
For example, the following iterator could be used to iterate over audio
|
||||
files:
|
||||
|
||||
\snippet code/src_corelib_io_qdiriterator.cpp 2
|
||||
\snippet code/src_corelib_io_qdirlisting.cpp 2
|
||||
|
||||
\note To list symlinks that point to non existing files, QDir::System must be
|
||||
passed to the flags.
|
||||
\note To list symlinks that point to non existing files, QDir::System
|
||||
must be set in \a flags.
|
||||
|
||||
\sa hasNext(), next(), IteratorFlags, QDir::setNameFilters()
|
||||
*/
|
||||
QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters,
|
||||
QDir::Filters filters, IteratorFlags flags)
|
||||
: d(new QDirIteratorPrivate)
|
||||
QDirListing::QDirListing(const QString &path, const QStringList &nameFilters, QDir::Filters filters,
|
||||
IteratorFlags flags)
|
||||
: d(new QDirListingPrivate)
|
||||
{
|
||||
d->dirEntry = QFileSystemEntry(path);
|
||||
d->initialEntryInfo.entry = QFileSystemEntry(path);
|
||||
d->nameFilters = nameFilters;
|
||||
d->filters = filters;
|
||||
d->iteratorFlags = flags;
|
||||
@ -467,109 +468,207 @@ QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters,
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the QDirIterator.
|
||||
Destroys the QDirListing.
|
||||
*/
|
||||
QDirIterator::~QDirIterator()
|
||||
QDirListing::~QDirListing() = default;
|
||||
|
||||
/*!
|
||||
Returns the directory path used to construct this QDirListing.
|
||||
*/
|
||||
QString QDirListing::iteratorPath() const
|
||||
{
|
||||
return d->initialEntryInfo.filePath();
|
||||
}
|
||||
|
||||
/*!
|
||||
Advances the iterator to the next entry, and returns the file path of this
|
||||
new entry. If hasNext() returns \c false, this function does nothing, and
|
||||
returns an empty QString.
|
||||
\fn QDirListing::const_iterator QDirListing::begin() const
|
||||
\fn QDirListing::const_iterator QDirListing::cbegin() const
|
||||
\fn QDirListing::const_iterator QDirListing::end() const
|
||||
\fn QDirListing::const_iterator QDirListing::cend() const
|
||||
|
||||
You can call fileName() or filePath() to get the current entry's file name
|
||||
or path, or fileInfo() to get a QFileInfo for the current entry.
|
||||
begin()/cbegin() returns a QDirListing::const_iterator that enables
|
||||
iterating over directory entries using a ranged-for loop; dereferencing
|
||||
this iterator returns a \c{const QFileInfo &}.
|
||||
|
||||
Call nextFileInfo() instead of next() if you're interested in the QFileInfo.
|
||||
end()/cend() return a sentinel const_iterator that signals the end of
|
||||
the iteration. Dereferencing this iterator is undefined behavior.
|
||||
|
||||
\sa hasNext(), nextFileInfo(), fileName(), filePath(), fileInfo()
|
||||
For example:
|
||||
\snippet code/src_corelib_io_qdirlisting.cpp 0
|
||||
|
||||
Here's how to find and read all files filtered by name, recursively:
|
||||
\snippet code/src_corelib_io_qdirlisting.cpp 1
|
||||
|
||||
\note As this is a unidirectional (forward-only) iterator, calling
|
||||
begin()/cbegin() more than once on the same QDirListing object could
|
||||
result in unexpected behavior (for example, some entries being skipped).
|
||||
|
||||
\sa fileInfo(), fileName(), filePath()
|
||||
*/
|
||||
QString QDirIterator::next()
|
||||
QDirListing::const_iterator QDirListing::begin() const
|
||||
{
|
||||
d->advance();
|
||||
return filePath();
|
||||
const_iterator it{d.get()};
|
||||
return ++it;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.3
|
||||
\fn const QDirListing::DirEntry &QDirListing::const_iterator::operator*() const
|
||||
|
||||
Advances the iterator to the next entry, and returns the file info of this
|
||||
new entry. If hasNext() returns \c false, this function does nothing, and
|
||||
returns an empty QFileInfo.
|
||||
|
||||
You can call fileName() or filePath() to get the current entry's file name
|
||||
or path, or fileInfo() to get a QFileInfo for the current entry.
|
||||
|
||||
Call next() instead of nextFileInfo() when all you need is the filePath().
|
||||
|
||||
\sa hasNext(), fileName(), filePath(), fileInfo()
|
||||
Returns a \c{const QDirListing::DirEntry &} of the directory entry this
|
||||
iterator points to.
|
||||
*/
|
||||
QFileInfo QDirIterator::nextFileInfo()
|
||||
|
||||
/*!
|
||||
\fn const QDirListing::DirEntry *QDirListing::const_iterator::operator->() const
|
||||
|
||||
Returns a \c{const QDirListing::DirEntry *} to the directory entry this
|
||||
iterator points to.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Advances the iterator and returns a reference to it.
|
||||
*/
|
||||
QDirListing::const_iterator &QDirListing::const_iterator::operator++()
|
||||
{
|
||||
d->advance();
|
||||
return fileInfo();
|
||||
dirListPtr->advance();
|
||||
if (!dirListPtr->hasIterators())
|
||||
*this = {}; // All done, make `this` the end() iterator
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if there is at least one more entry in the directory;
|
||||
otherwise, false is returned.
|
||||
\fn QFileInfo QDirListing::DirEntry::fileInfo() const
|
||||
\fn QString QDirListing::DirEntry::fileName() const
|
||||
\fn QString QDirListing::DirEntry::baseName() const
|
||||
\fn QString QDirListing::DirEntry::completeBaseName() const
|
||||
\fn QString QDirListing::DirEntry::suffix() const
|
||||
\fn QString QDirListing::DirEntry::bundleName() const
|
||||
\fn QString QDirListing::DirEntry::completeSuffix() const
|
||||
\fn QString QDirListing::DirEntry::filePath() const
|
||||
\fn QString QDirListing::DirEntry::canonicalFilePath() const
|
||||
\fn QString QDirListing::DirEntry::absoluteFilePath() const
|
||||
\fn QString QDirListing::DirEntry::absolutePath() const
|
||||
\fn bool QDirListing::DirEntry::isDir() const
|
||||
\fn bool QDirListing::DirEntry::isFile() const
|
||||
\fn bool QDirListing::DirEntry::isSymLink() const
|
||||
\fn bool QDirListing::DirEntry::exists() const
|
||||
\fn bool QDirListing::DirEntry::isHidden() const
|
||||
\fn bool QDirListing::DirEntry::isReadable() const
|
||||
\fn bool QDirListing::DirEntry::isWritable() const
|
||||
\fn bool QDirListing::DirEntry::isExecutable() const
|
||||
\fn qint64 QDirListing::DirEntry::size() const
|
||||
\fn QDateTime QDirListing::DirEntry::fileTime(QFile::FileTime type, const QTimeZone &tz) const
|
||||
\fn QDateTime QDirListing::DirEntry::birthTime(const QTimeZone &tz) const;
|
||||
\fn QDateTime QDirListing::DirEntry::metadataChangeTime(const QTimeZone &tz) const;
|
||||
\fn QDateTime QDirListing::DirEntry::lastModified(const QTimeZone &tz) const;
|
||||
\fn QDateTime QDirListing::DirEntry::lastRead(const QTimeZone &tz) const;
|
||||
|
||||
\sa next(), nextFileInfo(), fileName(), filePath(), fileInfo()
|
||||
See the QFileInfo methods with the same names.
|
||||
*/
|
||||
bool QDirIterator::hasNext() const
|
||||
|
||||
QFileInfo QDirListing::DirEntry::fileInfo() const
|
||||
{
|
||||
if (d->engine)
|
||||
return !d->fileEngineIterators.empty();
|
||||
else
|
||||
#ifndef QT_NO_FILESYSTEMITERATOR
|
||||
return !d->nativeIterators.empty();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
return dirListPtr->currentEntryInfo.fileInfo();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the file name for the current directory entry, without the path
|
||||
prepended.
|
||||
|
||||
This function is convenient when iterating a single directory. When using
|
||||
the QDirIterator::Subdirectories flag, you can use filePath() to get the
|
||||
full path.
|
||||
|
||||
\sa filePath(), fileInfo()
|
||||
*/
|
||||
QString QDirIterator::fileName() const
|
||||
QString QDirListing::DirEntry::fileName() const
|
||||
{
|
||||
return d->currentFileInfo.fileName();
|
||||
return dirListPtr->currentEntryInfo.fileName();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the full file path for the current directory entry.
|
||||
|
||||
\sa fileInfo(), fileName()
|
||||
*/
|
||||
QString QDirIterator::filePath() const
|
||||
QString QDirListing::DirEntry::baseName() const
|
||||
{
|
||||
return d->currentFileInfo.filePath();
|
||||
return dirListPtr->currentEntryInfo.baseName();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a QFileInfo for the current directory entry.
|
||||
|
||||
\sa filePath(), fileName()
|
||||
*/
|
||||
QFileInfo QDirIterator::fileInfo() const
|
||||
QString QDirListing::DirEntry::completeBaseName() const
|
||||
{
|
||||
return d->currentFileInfo;
|
||||
return dirListPtr->currentEntryInfo.completeBaseName();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the base directory of the iterator.
|
||||
*/
|
||||
QString QDirIterator::path() const
|
||||
QString QDirListing::DirEntry::suffix() const
|
||||
{
|
||||
return d->dirEntry.filePath();
|
||||
return dirListPtr->currentEntryInfo.suffix();
|
||||
}
|
||||
|
||||
QString QDirListing::DirEntry::bundleName() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.bundleName();
|
||||
}
|
||||
|
||||
QString QDirListing::DirEntry::completeSuffix() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.completeSuffix();
|
||||
}
|
||||
|
||||
QString QDirListing::DirEntry::filePath() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.filePath();
|
||||
}
|
||||
|
||||
QString QDirListing::DirEntry::canonicalFilePath() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.canonicalFilePath();
|
||||
}
|
||||
|
||||
QString QDirListing::DirEntry::absoluteFilePath() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.absoluteFilePath();
|
||||
}
|
||||
|
||||
QString QDirListing::DirEntry::absolutePath() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.absolutePath();
|
||||
}
|
||||
|
||||
bool QDirListing::DirEntry::isDir() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.isDir();
|
||||
}
|
||||
|
||||
bool QDirListing::DirEntry::isFile() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.isFile();
|
||||
}
|
||||
|
||||
bool QDirListing::DirEntry::isSymLink() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.isSymLink();
|
||||
}
|
||||
|
||||
bool QDirListing::DirEntry::exists() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.exists();
|
||||
}
|
||||
|
||||
bool QDirListing::DirEntry::isHidden() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.isHidden();
|
||||
}
|
||||
|
||||
bool QDirListing::DirEntry::isReadable() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.isReadable();
|
||||
}
|
||||
|
||||
bool QDirListing::DirEntry::isWritable() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.isWritable();
|
||||
}
|
||||
|
||||
bool QDirListing::DirEntry::isExecutable() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.isExecutable();
|
||||
}
|
||||
|
||||
qint64 QDirListing::DirEntry::size() const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.size();
|
||||
}
|
||||
|
||||
QDateTime QDirListing::DirEntry::fileTime(QFile::FileTime type, const QTimeZone &tz) const
|
||||
{
|
||||
return dirListPtr->currentEntryInfo.fileTime(type, tz);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -1,55 +1,119 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// Copyright (C) 2024 Ahmad Samir <a.samirh78@gmail.com>
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QDIRITERATOR_H
|
||||
#define QDIRITERATOR_H
|
||||
#ifndef QDILISTING_H
|
||||
#define QDILISTING_H
|
||||
|
||||
#include <QtCore/qdir.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QDirIteratorPrivate;
|
||||
class Q_CORE_EXPORT QDirIterator
|
||||
class QDirListingPrivate;
|
||||
|
||||
class Q_CORE_EXPORT QDirListing
|
||||
{
|
||||
public:
|
||||
enum IteratorFlag {
|
||||
NoIteratorFlags = 0x0,
|
||||
enum class IteratorFlag {
|
||||
NoFlag = 0x0,
|
||||
FollowSymlinks = 0x1,
|
||||
Subdirectories = 0x2
|
||||
Recursive = 0x2
|
||||
};
|
||||
Q_DECLARE_FLAGS(IteratorFlags, IteratorFlag)
|
||||
|
||||
QDirIterator(const QDir &dir, IteratorFlags flags = NoIteratorFlags);
|
||||
QDirIterator(const QString &path,
|
||||
IteratorFlags flags = NoIteratorFlags);
|
||||
QDirIterator(const QString &path,
|
||||
QDir::Filters filter,
|
||||
IteratorFlags flags = NoIteratorFlags);
|
||||
QDirIterator(const QString &path,
|
||||
const QStringList &nameFilters,
|
||||
QDir::Filters filters = QDir::NoFilter,
|
||||
IteratorFlags flags = NoIteratorFlags);
|
||||
QDirListing(const QDir &dir, IteratorFlags flags = IteratorFlag::NoFlag);
|
||||
QDirListing(const QString &path, IteratorFlags flags = IteratorFlag::NoFlag);
|
||||
QDirListing(const QString &path, QDir::Filters filter,
|
||||
IteratorFlags flags = IteratorFlag::NoFlag);
|
||||
QDirListing(const QString &path, const QStringList &nameFilters,
|
||||
QDir::Filters filters = QDir::NoFilter,
|
||||
IteratorFlags flags = IteratorFlag::NoFlag);
|
||||
|
||||
~QDirIterator();
|
||||
~QDirListing();
|
||||
|
||||
QString next();
|
||||
QFileInfo nextFileInfo();
|
||||
bool hasNext() const;
|
||||
QString iteratorPath() const;
|
||||
|
||||
QString fileName() const;
|
||||
QString filePath() const;
|
||||
QFileInfo fileInfo() const;
|
||||
QString path() const;
|
||||
class Q_CORE_EXPORT DirEntry
|
||||
{
|
||||
friend class QDirListing;
|
||||
QDirListingPrivate *dirListPtr = nullptr;
|
||||
public:
|
||||
QString fileName() const;
|
||||
QString baseName() const;
|
||||
QString completeBaseName() const;
|
||||
QString suffix() const;
|
||||
QString bundleName() const;
|
||||
QString completeSuffix() const;
|
||||
QString filePath() const;
|
||||
bool isDir() const;
|
||||
bool isFile() const;
|
||||
bool isSymLink() const;
|
||||
bool exists() const;
|
||||
bool isHidden() const;
|
||||
bool isReadable() const;
|
||||
bool isWritable() const;
|
||||
bool isExecutable() const;
|
||||
QFileInfo fileInfo() const;
|
||||
QString canonicalFilePath() const;
|
||||
QString absoluteFilePath() const;
|
||||
QString absolutePath() const;
|
||||
qint64 size() const;
|
||||
|
||||
QDateTime birthTime(const QTimeZone &tz) const { return fileTime(QFile::FileBirthTime, tz); }
|
||||
QDateTime metadataChangeTime(const QTimeZone &tz) const { return fileTime(QFile::FileMetadataChangeTime, tz); }
|
||||
QDateTime lastModified(const QTimeZone &tz) const { return fileTime(QFile::FileModificationTime, tz); }
|
||||
QDateTime lastRead(const QTimeZone &tz) const { return fileTime(QFile::FileAccessTime, tz); }
|
||||
QDateTime fileTime(QFile::FileTime type, const QTimeZone &tz) const;
|
||||
};
|
||||
|
||||
class const_iterator
|
||||
{
|
||||
friend class QDirListing;
|
||||
const_iterator(QDirListingPrivate *dp) : dirListPtr(dp) { dirEntry.dirListPtr = dp; }
|
||||
QDirListingPrivate *dirListPtr = nullptr;
|
||||
DirEntry dirEntry;
|
||||
public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = DirEntry;
|
||||
using difference_type = qint64;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
|
||||
const_iterator() = default;
|
||||
reference operator*() const { return dirEntry; }
|
||||
pointer operator->() const { return &dirEntry; }
|
||||
Q_CORE_EXPORT const_iterator &operator++();
|
||||
const_iterator operator++(int) { auto tmp = *this; operator++(); return tmp; };
|
||||
friend bool operator==(const const_iterator &lhs, const const_iterator &rhs)
|
||||
{
|
||||
// This is only used for the sentinel end iterator
|
||||
return lhs.dirListPtr == nullptr && rhs.dirListPtr == nullptr;
|
||||
}
|
||||
friend bool operator!=(const const_iterator &lhs, const const_iterator &rhs)
|
||||
{ return !(lhs == rhs); }
|
||||
};
|
||||
|
||||
const_iterator begin() const;
|
||||
const_iterator cbegin() const { return begin(); }
|
||||
const_iterator end() const { return {}; }
|
||||
const_iterator cend() const { return end(); }
|
||||
|
||||
// Qt compatibility
|
||||
const_iterator constBegin() const { return begin(); }
|
||||
const_iterator constEnd() const { return end(); }
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QDirIterator)
|
||||
Q_DISABLE_COPY(QDirListing)
|
||||
|
||||
QScopedPointer<QDirIteratorPrivate> d;
|
||||
std::unique_ptr<QDirListingPrivate> d;
|
||||
friend class QDir;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QDirIterator::IteratorFlags)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QDirListing::IteratorFlags)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
#endif // QDILISTING_H
|
||||
|
@ -22,6 +22,7 @@ class QFileInfoPrivate;
|
||||
class Q_CORE_EXPORT QFileInfo
|
||||
{
|
||||
friend class QDirIteratorPrivate;
|
||||
friend class QDirListingPrivate;
|
||||
public:
|
||||
explicit QFileInfo(QFileInfoPrivate *d);
|
||||
|
||||
|
@ -16,6 +16,7 @@ endif()
|
||||
add_subdirectory(qbuffer)
|
||||
add_subdirectory(qdataurl)
|
||||
add_subdirectory(qdiriterator)
|
||||
add_subdirectory(qdirlisting)
|
||||
add_subdirectory(qfile)
|
||||
add_subdirectory(largefile)
|
||||
add_subdirectory(qfileselector)
|
||||
|
1
tests/auto/corelib/io/qdirlisting/.gitignore
vendored
Normal file
1
tests/auto/corelib/io/qdirlisting/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
tst_qdirlisting
|
@ -2,44 +2,45 @@
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qdiriterator Test:
|
||||
## tst_qdirlisting Test:
|
||||
#####################################################################
|
||||
|
||||
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(tst_qdiriterator LANGUAGES CXX)
|
||||
project(tst_qdirlisting LANGUAGES CXX)
|
||||
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||
endif()
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "entrylist")
|
||||
|
||||
qt_internal_add_test(tst_qdiriterator
|
||||
qt_internal_add_test(tst_qdirlisting
|
||||
SOURCES
|
||||
tst_qdiriterator.cpp
|
||||
tst_qdirlisting.cpp
|
||||
LIBRARIES
|
||||
Qt::Core
|
||||
Qt::CorePrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set(qdiriterator_resource_files
|
||||
set(qdirlisting_resource_files
|
||||
"entrylist/directory/dummy"
|
||||
"entrylist/file"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qdiriterator "qdiriterator"
|
||||
qt_internal_add_resource(tst_qdirlisting "qdirlisting"
|
||||
PREFIX
|
||||
"/testdata/"
|
||||
FILES
|
||||
${qdiriterator_resource_files}
|
||||
${qdirlisting_resource_files}
|
||||
)
|
||||
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qdiriterator CONDITION CONFIG___contains___builtin_testdata
|
||||
qt_internal_extend_target(tst_qdirlisting CONDITION CONFIG___contains___builtin_testdata
|
||||
DEFINES
|
||||
BUILTIN_TESTDATA
|
||||
)
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qdebug.h>
|
||||
#include <qdiriterator.h>
|
||||
#include <qdirlisting.h>
|
||||
#include <qfileinfo.h>
|
||||
#include <qstringlist.h>
|
||||
#include <QSet>
|
||||
@ -25,10 +25,12 @@
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
Q_DECLARE_METATYPE(QDirIterator::IteratorFlags)
|
||||
Q_DECLARE_METATYPE(QDirListing::IteratorFlags)
|
||||
Q_DECLARE_METATYPE(QDir::Filters)
|
||||
|
||||
class tst_QDirIterator : public QObject
|
||||
using ItFlag = QDirListing::IteratorFlag;
|
||||
|
||||
class tst_QDirListing : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@ -90,23 +92,21 @@ private:
|
||||
QTemporaryDir m_dataDir;
|
||||
};
|
||||
|
||||
void tst_QDirIterator::initTestCase()
|
||||
void tst_QDirListing::initTestCase()
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
QString testdata_dir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
QString resourceSourcePath = QStringLiteral(":/testdata");
|
||||
QDirIterator it(resourceSourcePath, QDirIterator::Subdirectories);
|
||||
while (it.hasNext()) {
|
||||
QFileInfo fileInfo = it.nextFileInfo();
|
||||
|
||||
if (!fileInfo.isDir()) {
|
||||
for (const auto &dirEntry : QDirListing(resourceSourcePath, ItFlag::Recursive)) {
|
||||
if (!dirEntry.isDir()) {
|
||||
const QString &filePath = dirEntry.filePath();
|
||||
QString destination = testdata_dir + QLatin1Char('/')
|
||||
+ fileInfo.filePath().mid(resourceSourcePath.length());
|
||||
+ filePath.sliced(resourceSourcePath.length());
|
||||
QFileInfo destinationFileInfo(destination);
|
||||
if (!destinationFileInfo.exists()) {
|
||||
QDir().mkpath(destinationFileInfo.path());
|
||||
if (!QFile::copy(fileInfo.filePath(), destination))
|
||||
qWarning("Failed to copy %s", qPrintable(fileInfo.filePath()));
|
||||
if (!QFile::copy(filePath, destination))
|
||||
qWarning("Failed to copy %s", qPrintable(filePath));
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,16 +180,16 @@ void tst_QDirIterator::initTestCase()
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||
void tst_QDirListing::iterateRelativeDirectory_data()
|
||||
{
|
||||
QTest::addColumn<QString>("dirName"); // relative from current path or abs
|
||||
QTest::addColumn<QDirIterator::IteratorFlags>("flags");
|
||||
QTest::addColumn<QDirListing::IteratorFlags>("flags");
|
||||
QTest::addColumn<QDir::Filters>("filters");
|
||||
QTest::addColumn<QStringList>("nameFilters");
|
||||
QTest::addColumn<QStringList>("entries");
|
||||
|
||||
QTest::newRow("no flags")
|
||||
<< QString("entrylist") << QDirIterator::IteratorFlags{}
|
||||
<< QString("entrylist") << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::NoFilter) << QStringList("*")
|
||||
<< QString(
|
||||
"entrylist/.,"
|
||||
@ -205,7 +205,7 @@ void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||
"entrylist/writable").split(',');
|
||||
|
||||
QTest::newRow("NoDot")
|
||||
<< QString("entrylist") << QDirIterator::IteratorFlags{}
|
||||
<< QString("entrylist") << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::AllEntries | QDir::NoDot) << QStringList("*")
|
||||
<< QString(
|
||||
"entrylist/..,"
|
||||
@ -220,7 +220,7 @@ void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||
"entrylist/writable").split(',');
|
||||
|
||||
QTest::newRow("NoDotDot")
|
||||
<< QString("entrylist") << QDirIterator::IteratorFlags{}
|
||||
<< QString("entrylist") << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::AllEntries | QDir::NoDotDot) << QStringList("*")
|
||||
<< QString(
|
||||
"entrylist/.,"
|
||||
@ -235,7 +235,7 @@ void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||
"entrylist/writable").split(',');
|
||||
|
||||
QTest::newRow("NoDotAndDotDot")
|
||||
<< QString("entrylist") << QDirIterator::IteratorFlags{}
|
||||
<< QString("entrylist") << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::AllEntries | QDir::NoDotAndDotDot) << QStringList("*")
|
||||
<< QString(
|
||||
"entrylist/file,"
|
||||
@ -249,7 +249,7 @@ void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||
"entrylist/writable").split(',');
|
||||
|
||||
QTest::newRow("QDir::Subdirectories | QDir::FollowSymlinks")
|
||||
<< QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories | QDirIterator::FollowSymlinks)
|
||||
<< QString("entrylist") << QDirListing::IteratorFlags(ItFlag::Recursive | ItFlag::FollowSymlinks)
|
||||
<< QDir::Filters(QDir::NoFilter) << QStringList("*")
|
||||
<< QString(
|
||||
"entrylist/.,"
|
||||
@ -268,7 +268,7 @@ void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||
"entrylist/writable").split(',');
|
||||
|
||||
QTest::newRow("QDir::Subdirectories / QDir::Files")
|
||||
<< QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories)
|
||||
<< QString("entrylist") << QDirListing::IteratorFlags(ItFlag::Recursive)
|
||||
<< QDir::Filters(QDir::Files) << QStringList("*")
|
||||
<< QString("entrylist/directory/dummy,"
|
||||
"entrylist/file,"
|
||||
@ -278,7 +278,7 @@ void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||
"entrylist/writable").split(',');
|
||||
|
||||
QTest::newRow("QDir::Subdirectories | QDir::FollowSymlinks / QDir::Files")
|
||||
<< QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories | QDirIterator::FollowSymlinks)
|
||||
<< QString("entrylist") << QDirListing::IteratorFlags(ItFlag::Recursive | QDirListing::IteratorFlag::FollowSymlinks)
|
||||
<< QDir::Filters(QDir::Files) << QStringList("*")
|
||||
<< QString("entrylist/file,"
|
||||
#ifndef Q_NO_SYMLINKS
|
||||
@ -288,47 +288,31 @@ void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||
"entrylist/writable").split(',');
|
||||
|
||||
QTest::newRow("empty, default")
|
||||
<< QString("empty") << QDirIterator::IteratorFlags{}
|
||||
<< QString("empty") << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::NoFilter) << QStringList("*")
|
||||
<< QString("empty/.,empty/..").split(',');
|
||||
|
||||
QTest::newRow("empty, QDir::NoDotAndDotDot")
|
||||
<< QString("empty") << QDirIterator::IteratorFlags{}
|
||||
<< QString("empty") << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::NoDotAndDotDot) << QStringList("*")
|
||||
<< QStringList();
|
||||
}
|
||||
|
||||
void tst_QDirIterator::iterateRelativeDirectory()
|
||||
void tst_QDirListing::iterateRelativeDirectory()
|
||||
{
|
||||
QFETCH(QString, dirName);
|
||||
QFETCH(QDirIterator::IteratorFlags, flags);
|
||||
QFETCH(QDirListing::IteratorFlags, flags);
|
||||
QFETCH(QDir::Filters, filters);
|
||||
QFETCH(QStringList, nameFilters);
|
||||
QFETCH(const QStringList, entries);
|
||||
|
||||
QDirIterator it(dirName, nameFilters, filters, flags);
|
||||
QStringList list;
|
||||
while (it.hasNext()) {
|
||||
QString next = it.next();
|
||||
|
||||
QString fileName = it.fileName();
|
||||
QString filePath = it.filePath();
|
||||
QString path = it.path();
|
||||
|
||||
QFileInfo info = it.fileInfo();
|
||||
|
||||
QCOMPARE(path, dirName);
|
||||
QCOMPARE(next, filePath);
|
||||
|
||||
QCOMPARE(info, QFileInfo(next));
|
||||
QCOMPARE(fileName, info.fileName());
|
||||
QCOMPARE(filePath, info.filePath());
|
||||
|
||||
for (const auto &dirEntry : QDirListing(dirName, nameFilters, filters, flags)) {
|
||||
// Using canonical file paths for final comparison
|
||||
list << info.canonicalFilePath();
|
||||
list << dirEntry.fileInfo().canonicalFilePath();
|
||||
}
|
||||
|
||||
// The order of items returned by QDirIterator is not guaranteed.
|
||||
// The order of items returned by QDirListing is not guaranteed.
|
||||
list.sort();
|
||||
|
||||
QStringList sortedEntries;
|
||||
@ -344,43 +328,42 @@ void tst_QDirIterator::iterateRelativeDirectory()
|
||||
QCOMPARE(list, sortedEntries);
|
||||
}
|
||||
|
||||
void tst_QDirIterator::iterateResource_data()
|
||||
void tst_QDirListing::iterateResource_data()
|
||||
{
|
||||
QTest::addColumn<QString>("dirName"); // relative from current path or abs
|
||||
QTest::addColumn<QDirIterator::IteratorFlags>("flags");
|
||||
QTest::addColumn<QDirListing::IteratorFlags>("flags");
|
||||
QTest::addColumn<QDir::Filters>("filters");
|
||||
QTest::addColumn<QStringList>("nameFilters");
|
||||
QTest::addColumn<QStringList>("entries");
|
||||
|
||||
QTest::newRow("invalid") << QString::fromLatin1(":/testdata/burpaburpa") << QDirIterator::IteratorFlags{}
|
||||
QTest::newRow("invalid") << QString::fromLatin1(":/testdata/burpaburpa") << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
|
||||
<< QStringList();
|
||||
QTest::newRow("qrc:/testdata") << u":/testdata/"_s << QDirIterator::IteratorFlags{}
|
||||
QTest::newRow("qrc:/testdata") << u":/testdata/"_s << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
|
||||
<< QString::fromLatin1(":/testdata/entrylist").split(QLatin1String(","));
|
||||
QTest::newRow("qrc:/testdata/entrylist") << u":/testdata/entrylist"_s << QDirIterator::IteratorFlags{}
|
||||
QTest::newRow("qrc:/testdata/entrylist") << u":/testdata/entrylist"_s << QDirListing::IteratorFlags{}
|
||||
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
|
||||
<< QString::fromLatin1(":/testdata/entrylist/directory,:/testdata/entrylist/file").split(QLatin1String(","));
|
||||
QTest::newRow("qrc:/testdata recursive") << u":/testdata"_s
|
||||
<< QDirIterator::IteratorFlags(QDirIterator::Subdirectories)
|
||||
<< QDirListing::IteratorFlags(ItFlag::Recursive)
|
||||
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
|
||||
<< QString::fromLatin1(":/testdata/entrylist,:/testdata/entrylist/directory,:/testdata/entrylist/directory/dummy,:/testdata/entrylist/file").split(QLatin1String(","));
|
||||
}
|
||||
|
||||
void tst_QDirIterator::iterateResource()
|
||||
void tst_QDirListing::iterateResource()
|
||||
{
|
||||
QFETCH(QString, dirName);
|
||||
QFETCH(QDirIterator::IteratorFlags, flags);
|
||||
QFETCH(QDirListing::IteratorFlags, flags);
|
||||
QFETCH(QDir::Filters, filters);
|
||||
QFETCH(QStringList, nameFilters);
|
||||
QFETCH(QStringList, entries);
|
||||
|
||||
QDirIterator it(dirName, nameFilters, filters, flags);
|
||||
QStringList list;
|
||||
while (it.hasNext()) {
|
||||
const QString dir = it.next();
|
||||
for (const auto &dirEntry : QDirListing(dirName, nameFilters, filters, flags)) {
|
||||
QString dir = dirEntry.fileInfo().filePath();
|
||||
if (!dir.startsWith(":/qt-project.org"))
|
||||
list << dir;
|
||||
list.emplace_back(std::move(dir));
|
||||
}
|
||||
|
||||
list.sort();
|
||||
@ -395,7 +378,7 @@ void tst_QDirIterator::iterateResource()
|
||||
QCOMPARE(list, sortedEntries);
|
||||
}
|
||||
|
||||
void tst_QDirIterator::stopLinkLoop()
|
||||
void tst_QDirListing::stopLinkLoop()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
// ### Sadly, this is a platform difference right now.
|
||||
@ -418,12 +401,14 @@ void tst_QDirIterator::stopLinkLoop()
|
||||
createLink("..", "entrylist/directory/entrylist4.lnk");
|
||||
#endif
|
||||
|
||||
QDirIterator it(QLatin1String("entrylist"), QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||
constexpr auto flags = ItFlag::Recursive | ItFlag::FollowSymlinks;
|
||||
QDirListing dirIter(u"entrylist"_s, flags);
|
||||
QStringList list;
|
||||
int max = 200;
|
||||
while (--max && it.hasNext())
|
||||
it.nextFileInfo();
|
||||
QVERIFY(max);
|
||||
auto it = dirIter.begin();
|
||||
while (--max && it != dirIter.end())
|
||||
++it;
|
||||
QCOMPARE_GT(max, 0);
|
||||
|
||||
// The goal of this test is only to ensure that the test above don't malfunction
|
||||
}
|
||||
@ -451,7 +436,7 @@ public:
|
||||
#endif
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
void tst_QDirIterator::engineWithNoIterator()
|
||||
void tst_QDirListing::engineWithNoIterator()
|
||||
{
|
||||
EngineWithNoIteratorHandler handler;
|
||||
|
||||
@ -464,113 +449,98 @@ class CustomEngineHandler : public QAbstractFileEngineHandler
|
||||
public:
|
||||
QAbstractFileEngine *create(const QString &fileName) const override
|
||||
{
|
||||
// We want to test QFSFileEngine specifically, so force QDirIterator to use it
|
||||
// We want to test QFSFileEngine specifically, so force QDirListing to use it
|
||||
// over the default QFileSystemEngine
|
||||
return new QFSFileEngine(fileName);
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QDirIterator::testQFsFileEngineIterator()
|
||||
void tst_QDirListing::testQFsFileEngineIterator()
|
||||
{
|
||||
QFETCH(QString, dirName);
|
||||
QFETCH(QStringList, nameFilters);
|
||||
QFETCH(QDir::Filters, filters);
|
||||
QFETCH(QDirIterator::IteratorFlags, flags);
|
||||
QFETCH(QDirListing::IteratorFlags, flags);
|
||||
|
||||
if (dirName == u"empty")
|
||||
return; // This row isn't useful in this test
|
||||
|
||||
CustomEngineHandler handler;
|
||||
bool isEmpty = true;
|
||||
QDirIterator iter(dirName, nameFilters, filters, flags);
|
||||
while (iter.hasNext()) {
|
||||
const QFileInfo &fi = iter.nextFileInfo();
|
||||
if (fi.filePath().contains(u"entrylist"))
|
||||
for (const auto &dirEntry : QDirListing(u"entrylist"_s, nameFilters, filters, flags)) {
|
||||
if (dirEntry.filePath().contains(u"entrylist"))
|
||||
isEmpty = false; // At least one entry in `entrylist` dir
|
||||
}
|
||||
QVERIFY(!isEmpty);
|
||||
QVERIFY(!isEmpty); // At least one entry
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QDirIterator::absoluteFilePathsFromRelativeIteratorPath()
|
||||
void tst_QDirListing::absoluteFilePathsFromRelativeIteratorPath()
|
||||
{
|
||||
QDirIterator it("entrylist/", QDir::NoDotAndDotDot);
|
||||
while (it.hasNext())
|
||||
QVERIFY(it.nextFileInfo().absoluteFilePath().contains("entrylist"));
|
||||
for (const auto &dirEntry : QDirListing(u"entrylist/"_s, QDir::NoDotAndDotDot))
|
||||
QVERIFY(dirEntry.absoluteFilePath().contains("entrylist"));
|
||||
}
|
||||
|
||||
void tst_QDirIterator::recurseWithFilters() const
|
||||
void tst_QDirListing::recurseWithFilters() const
|
||||
{
|
||||
QStringList nameFilters;
|
||||
nameFilters.append("*.txt");
|
||||
|
||||
QDirIterator it("recursiveDirs/", nameFilters, QDir::Files,
|
||||
QDirIterator::Subdirectories);
|
||||
|
||||
QSet<QString> actualEntries;
|
||||
QSet<QString> expectedEntries;
|
||||
expectedEntries.insert(QString::fromLatin1("recursiveDirs/dir1/textFileB.txt"));
|
||||
expectedEntries.insert(QString::fromLatin1("recursiveDirs/textFileA.txt"));
|
||||
|
||||
QVERIFY(it.hasNext());
|
||||
actualEntries.insert(it.next());
|
||||
QVERIFY(it.hasNext());
|
||||
actualEntries.insert(it.next());
|
||||
QCOMPARE(actualEntries, expectedEntries);
|
||||
for (const auto &dirEntry : QDirListing(u"recursiveDirs/"_s, QStringList{u"*.txt"_s},
|
||||
QDir::Files, ItFlag::Recursive)) {
|
||||
actualEntries.insert(dirEntry.filePath());
|
||||
}
|
||||
|
||||
QVERIFY(!it.hasNext());
|
||||
QCOMPARE(actualEntries, expectedEntries);
|
||||
}
|
||||
|
||||
void tst_QDirIterator::longPath()
|
||||
void tst_QDirListing::longPath()
|
||||
{
|
||||
QDir dir;
|
||||
dir.mkdir("longpaths");
|
||||
dir.cd("longpaths");
|
||||
|
||||
QString dirName = "x";
|
||||
int n = 0;
|
||||
qsizetype n = 0;
|
||||
while (dir.exists(dirName) || dir.mkdir(dirName)) {
|
||||
++n;
|
||||
dirName.append('x');
|
||||
}
|
||||
|
||||
QDirIterator it(dir.absolutePath(), QDir::NoDotAndDotDot|QDir::Dirs, QDirIterator::Subdirectories);
|
||||
int m = 0;
|
||||
while (it.hasNext()) {
|
||||
QDirListing dirList(dir.absolutePath(), QDir::NoDotAndDotDot|QDir::Dirs, ItFlag::Recursive);
|
||||
qsizetype m = 0;
|
||||
for (auto it = dirList.begin(); it != dirList.end(); ++it)
|
||||
++m;
|
||||
it.nextFileInfo();
|
||||
}
|
||||
|
||||
QCOMPARE(n, m);
|
||||
|
||||
dirName.chop(1);
|
||||
while (dirName.size() > 0 && dir.exists(dirName) && dir.rmdir(dirName)) {
|
||||
while (dirName.size() > 0 && dir.exists(dirName) && dir.rmdir(dirName))
|
||||
dirName.chop(1);
|
||||
}
|
||||
|
||||
dir.cdUp();
|
||||
dir.rmdir("longpaths");
|
||||
}
|
||||
|
||||
void tst_QDirIterator::dirorder()
|
||||
void tst_QDirListing::dirorder()
|
||||
{
|
||||
QDirIterator iterator("foo", QDirIterator::Subdirectories);
|
||||
while (iterator.hasNext() && iterator.next() != "foo/bar")
|
||||
{ }
|
||||
QStringList entries;
|
||||
for (const auto &dirEntry : QDirListing(u"foo"_s, ItFlag::Recursive))
|
||||
entries.append(dirEntry.filePath());
|
||||
|
||||
QCOMPARE(iterator.filePath(), QString("foo/bar"));
|
||||
QCOMPARE(iterator.fileInfo().filePath(), QString("foo/bar"));
|
||||
QCOMPARE_GT(entries.indexOf(u"foo/bar"_s), entries.indexOf(u"foo"_s));
|
||||
}
|
||||
|
||||
void tst_QDirIterator::relativePaths()
|
||||
void tst_QDirListing::relativePaths()
|
||||
{
|
||||
QDirIterator iterator("*", QDirIterator::Subdirectories);
|
||||
while(iterator.hasNext()) {
|
||||
QCOMPARE(iterator.filePath(), QDir::cleanPath(iterator.filePath()));
|
||||
}
|
||||
for (const auto &dirEntry : QDirListing(u"*"_s, ItFlag::Recursive))
|
||||
QCOMPARE(dirEntry.filePath(), QDir::cleanPath(dirEntry.filePath()));
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
void tst_QDirIterator::uncPaths_data()
|
||||
void tst_QDirListing::uncPaths_data()
|
||||
{
|
||||
QTest::addColumn<QString>("dirName");
|
||||
QTest::newRow("uncserver")
|
||||
@ -580,13 +550,13 @@ void tst_QDirIterator::uncPaths_data()
|
||||
QTest::newRow("uncserver/testshare/tmp")
|
||||
<<QString("//" + QTest::uncServerName() + "/testshare/tmp");
|
||||
}
|
||||
void tst_QDirIterator::uncPaths()
|
||||
void tst_QDirListing::uncPaths()
|
||||
{
|
||||
QFETCH(QString, dirName);
|
||||
QDirIterator iterator(dirName, QDir::AllEntries|QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
|
||||
while(iterator.hasNext()) {
|
||||
iterator.next();
|
||||
QCOMPARE(iterator.filePath(), QDir::cleanPath(iterator.filePath()));
|
||||
constexpr auto dirFilters = QDir::AllEntries | QDir::NoDotAndDotDot;
|
||||
for (const auto &dirEntry : QDirListing(dirName, dirFilters, ItFlag::Recursive)) {
|
||||
const QString &filePath = dirEntry.filePath();
|
||||
QCOMPARE(filePath, QDir::cleanPath(filePath));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -596,16 +566,17 @@ void tst_QDirIterator::uncPaths()
|
||||
// a special call since hidden files need to be "marked" while in Unix
|
||||
// anything starting by a '.' is a hidden file.
|
||||
// For that reason this test is not run in Windows.
|
||||
void tst_QDirIterator::hiddenDirs_hiddenFiles()
|
||||
void tst_QDirListing::hiddenDirs_hiddenFiles()
|
||||
{
|
||||
// Only files
|
||||
{
|
||||
int matches = 0;
|
||||
int failures = 0;
|
||||
QDirIterator di("hiddenDirs_hiddenFiles", QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
|
||||
while (di.hasNext()) {
|
||||
constexpr auto filters = QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot;
|
||||
for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, filters,
|
||||
ItFlag::Recursive)) {
|
||||
++matches;
|
||||
if (di.nextFileInfo().isDir())
|
||||
if (dirEntry.isDir())
|
||||
++failures; // search was only supposed to find files
|
||||
}
|
||||
QCOMPARE(matches, 6);
|
||||
@ -615,10 +586,11 @@ void tst_QDirIterator::hiddenDirs_hiddenFiles()
|
||||
{
|
||||
int matches = 0;
|
||||
int failures = 0;
|
||||
QDirIterator di("hiddenDirs_hiddenFiles", QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
|
||||
while (di.hasNext()) {
|
||||
constexpr auto filters = QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot;
|
||||
for (const auto &dirEntry : QDirListing(u"hiddenDirs_hiddenFiles"_s, filters,
|
||||
ItFlag::Recursive)) {
|
||||
++matches;
|
||||
if (!di.nextFileInfo().isDir())
|
||||
if (!dirEntry.isDir())
|
||||
++failures; // search was only supposed to find files
|
||||
}
|
||||
QCOMPARE(matches, 6);
|
||||
@ -627,7 +599,7 @@ void tst_QDirIterator::hiddenDirs_hiddenFiles()
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
QTEST_MAIN(tst_QDirIterator)
|
||||
QTEST_MAIN(tst_QDirListing)
|
||||
|
||||
#include "tst_qdiriterator.moc"
|
||||
#include "tst_qdirlisting.moc"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user