Copy QDirIterator.{cpp,h} to QDirListing.{cpp,h}
To make it easier to follow the history in git. Change-Id: I094056c1ec130aeef77aa2d20289ab766bc25083 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
6c424dbcb0
commit
78c33a7741
28
src/corelib/doc/snippets/code/src_corelib_io_qdirlisting.cpp
Normal file
28
src/corelib/doc/snippets/code/src_corelib_io_qdirlisting.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||||
|
|
||||||
|
//! [0]
|
||||||
|
QDirIterator it("/etc", QDirIterator::Subdirectories);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QString dir = it.next();
|
||||||
|
qDebug() << dir;
|
||||||
|
// /etc/.
|
||||||
|
// /etc/..
|
||||||
|
// /etc/X11
|
||||||
|
// /etc/X11/fs
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
//! [0]
|
||||||
|
|
||||||
|
//! [1]
|
||||||
|
QDirIterator it("/sys", QStringList() << "scaling_cur_freq", QDir::NoFilter, QDirIterator::Subdirectories);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QFile f(it.next());
|
||||||
|
f.open(QIODevice::ReadOnly);
|
||||||
|
qDebug() << f.fileName() << f.readAll().trimmed().toDouble() / 1000 << "MHz";
|
||||||
|
}
|
||||||
|
//! [1]
|
||||||
|
|
||||||
|
//! [2]
|
||||||
|
QDirIterator audioFileIt(audioPath, {"*.mp3", "*.wav"}, QDir::Files);
|
||||||
|
//! [2]
|
575
src/corelib/io/qdirlisting.cpp
Normal file
575
src/corelib/io/qdirlisting.cpp
Normal file
@ -0,0 +1,575 @@
|
|||||||
|
// Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
// 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
|
||||||
|
\inmodule QtCore
|
||||||
|
\brief The QDirIterator class provides an iterator for directory entrylists.
|
||||||
|
|
||||||
|
You can use QDirIterator 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.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
\snippet code/src_corelib_io_qdiriterator.cpp 0
|
||||||
|
|
||||||
|
Here's how to find and read all files filtered by name, recursively:
|
||||||
|
|
||||||
|
\snippet code/src_corelib_io_qdiriterator.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.
|
||||||
|
|
||||||
|
Unlike Qt's container iterators, QDirIterator is uni-directional (i.e.,
|
||||||
|
you cannot iterate directories in reverse order) and does not allow random
|
||||||
|
access.
|
||||||
|
|
||||||
|
\sa QDir, QDir::entryList()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \enum QDirIterator::IteratorFlag
|
||||||
|
|
||||||
|
This enum describes flags that you can combine to configure the behavior
|
||||||
|
of QDirIterator.
|
||||||
|
|
||||||
|
\value NoIteratorFlags 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 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qdiriterator.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
|
||||||
|
|
||||||
|
#include <QtCore/private/qfilesystemiterator_p.h>
|
||||||
|
#include <QtCore/private/qfilesystementry_p.h>
|
||||||
|
#include <QtCore/private/qfilesystemmetadata_p.h>
|
||||||
|
#include <QtCore/private/qfilesystemengine_p.h>
|
||||||
|
#include <QtCore/private/qfileinfo_p.h>
|
||||||
|
#include <QtCore/private/qduplicatetracker_p.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stack>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
class QDirIteratorPrivate
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
\sa hasNext(), next(), IteratorFlags
|
||||||
|
*/
|
||||||
|
QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags)
|
||||||
|
: d(new QDirIteratorPrivate)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
\sa hasNext(), next(), IteratorFlags
|
||||||
|
*/
|
||||||
|
QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags)
|
||||||
|
: d(new QDirIteratorPrivate)
|
||||||
|
{
|
||||||
|
d->dirEntry = QFileSystemEntry(path);
|
||||||
|
d->filters = filters;
|
||||||
|
d->iteratorFlags = flags;
|
||||||
|
d->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Constructs a QDirIterator 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)
|
||||||
|
{
|
||||||
|
d->dirEntry = QFileSystemEntry(path);
|
||||||
|
d->filters = QDir::NoFilter;
|
||||||
|
d->iteratorFlags = flags;
|
||||||
|
d->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Constructs a QDirIterator 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.
|
||||||
|
|
||||||
|
By default, \a flags is NoIteratorFlags, which provides the same behavior
|
||||||
|
as QDir::entryList().
|
||||||
|
|
||||||
|
For example, the following iterator could be used to iterate over audio
|
||||||
|
files:
|
||||||
|
|
||||||
|
\snippet code/src_corelib_io_qdiriterator.cpp 2
|
||||||
|
|
||||||
|
\note To list symlinks that point to non existing files, QDir::System must be
|
||||||
|
passed to the flags.
|
||||||
|
|
||||||
|
\sa hasNext(), next(), IteratorFlags, QDir::setNameFilters()
|
||||||
|
*/
|
||||||
|
QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters,
|
||||||
|
QDir::Filters filters, IteratorFlags flags)
|
||||||
|
: d(new QDirIteratorPrivate)
|
||||||
|
{
|
||||||
|
d->dirEntry = QFileSystemEntry(path);
|
||||||
|
d->nameFilters = nameFilters;
|
||||||
|
d->filters = filters;
|
||||||
|
d->iteratorFlags = flags;
|
||||||
|
d->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Destroys the QDirIterator.
|
||||||
|
*/
|
||||||
|
QDirIterator::~QDirIterator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 nextFileInfo() instead of next() if you're interested in the QFileInfo.
|
||||||
|
|
||||||
|
\sa hasNext(), nextFileInfo(), fileName(), filePath(), fileInfo()
|
||||||
|
*/
|
||||||
|
QString QDirIterator::next()
|
||||||
|
{
|
||||||
|
d->advance();
|
||||||
|
return filePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 6.3
|
||||||
|
|
||||||
|
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()
|
||||||
|
*/
|
||||||
|
QFileInfo QDirIterator::nextFileInfo()
|
||||||
|
{
|
||||||
|
d->advance();
|
||||||
|
return fileInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns \c true if there is at least one more entry in the directory;
|
||||||
|
otherwise, false is returned.
|
||||||
|
|
||||||
|
\sa next(), nextFileInfo(), fileName(), filePath(), fileInfo()
|
||||||
|
*/
|
||||||
|
bool QDirIterator::hasNext() const
|
||||||
|
{
|
||||||
|
if (d->engine)
|
||||||
|
return !d->fileEngineIterators.empty();
|
||||||
|
else
|
||||||
|
#ifndef QT_NO_FILESYSTEMITERATOR
|
||||||
|
return !d->nativeIterators.empty();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
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
|
||||||
|
{
|
||||||
|
return d->currentFileInfo.fileName();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the full file path for the current directory entry.
|
||||||
|
|
||||||
|
\sa fileInfo(), fileName()
|
||||||
|
*/
|
||||||
|
QString QDirIterator::filePath() const
|
||||||
|
{
|
||||||
|
return d->currentFileInfo.filePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns a QFileInfo for the current directory entry.
|
||||||
|
|
||||||
|
\sa filePath(), fileName()
|
||||||
|
*/
|
||||||
|
QFileInfo QDirIterator::fileInfo() const
|
||||||
|
{
|
||||||
|
return d->currentFileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the base directory of the iterator.
|
||||||
|
*/
|
||||||
|
QString QDirIterator::path() const
|
||||||
|
{
|
||||||
|
return d->dirEntry.filePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
55
src/corelib/io/qdirlisting.h
Normal file
55
src/corelib/io/qdirlisting.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (C) 2016 The Qt Company Ltd.
|
||||||
|
// 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
|
||||||
|
|
||||||
|
#include <QtCore/qdir.h>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class QDirIteratorPrivate;
|
||||||
|
class Q_CORE_EXPORT QDirIterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum IteratorFlag {
|
||||||
|
NoIteratorFlags = 0x0,
|
||||||
|
FollowSymlinks = 0x1,
|
||||||
|
Subdirectories = 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);
|
||||||
|
|
||||||
|
~QDirIterator();
|
||||||
|
|
||||||
|
QString next();
|
||||||
|
QFileInfo nextFileInfo();
|
||||||
|
bool hasNext() const;
|
||||||
|
|
||||||
|
QString fileName() const;
|
||||||
|
QString filePath() const;
|
||||||
|
QFileInfo fileInfo() const;
|
||||||
|
QString path() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(QDirIterator)
|
||||||
|
|
||||||
|
QScopedPointer<QDirIteratorPrivate> d;
|
||||||
|
friend class QDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_OPERATORS_FOR_FLAGS(QDirIterator::IteratorFlags)
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif
|
45
tests/auto/corelib/io/qdirlisting/CMakeLists.txt
Normal file
45
tests/auto/corelib/io/qdirlisting/CMakeLists.txt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Copyright (C) 2022 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
## tst_qdiriterator Test:
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(tst_qdiriterator LANGUAGES CXX)
|
||||||
|
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Collect test data
|
||||||
|
list(APPEND test_data "entrylist")
|
||||||
|
|
||||||
|
qt_internal_add_test(tst_qdiriterator
|
||||||
|
SOURCES
|
||||||
|
tst_qdiriterator.cpp
|
||||||
|
LIBRARIES
|
||||||
|
Qt::CorePrivate
|
||||||
|
TESTDATA ${test_data}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Resources:
|
||||||
|
set(qdiriterator_resource_files
|
||||||
|
"entrylist/directory/dummy"
|
||||||
|
"entrylist/file"
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_internal_add_resource(tst_qdiriterator "qdiriterator"
|
||||||
|
PREFIX
|
||||||
|
"/testdata/"
|
||||||
|
FILES
|
||||||
|
${qdiriterator_resource_files}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
## Scopes:
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
qt_internal_extend_target(tst_qdiriterator CONDITION CONFIG___contains___builtin_testdata
|
||||||
|
DEFINES
|
||||||
|
BUILTIN_TESTDATA
|
||||||
|
)
|
0
tests/auto/corelib/io/qdirlisting/entrylist/file
Normal file
0
tests/auto/corelib/io/qdirlisting/entrylist/file
Normal file
633
tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp
Normal file
633
tests/auto/corelib/io/qdirlisting/tst_qdirlisting.cpp
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
// Copyright (C) 2021 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <qcoreapplication.h>
|
||||||
|
#include <qdebug.h>
|
||||||
|
#include <qdiriterator.h>
|
||||||
|
#include <qfileinfo.h>
|
||||||
|
#include <qstringlist.h>
|
||||||
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
#include <QtCore/private/qfsfileengine_p.h>
|
||||||
|
|
||||||
|
#if defined(Q_OS_VXWORKS)
|
||||||
|
#define Q_NO_SYMLINKS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "../../../../shared/filesystem.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_ANDROID
|
||||||
|
#include <QStandardPaths>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QDirIterator::IteratorFlags)
|
||||||
|
Q_DECLARE_METATYPE(QDir::Filters)
|
||||||
|
|
||||||
|
class tst_QDirIterator : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private: // convenience functions
|
||||||
|
QStringList createdDirectories;
|
||||||
|
QStringList createdFiles;
|
||||||
|
|
||||||
|
QDir currentDir;
|
||||||
|
bool createDirectory(const QString &dirName)
|
||||||
|
{
|
||||||
|
if (currentDir.mkdir(dirName)) {
|
||||||
|
createdDirectories.prepend(dirName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool createFile(const QString &fileName)
|
||||||
|
{
|
||||||
|
QFile file(fileName);
|
||||||
|
return file.open(QIODevice::WriteOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool createLink(const QString &destination, const QString &linkName)
|
||||||
|
{
|
||||||
|
if (QFile::link(destination, linkName)) {
|
||||||
|
createdFiles << linkName;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void iterateRelativeDirectory_data();
|
||||||
|
void iterateRelativeDirectory();
|
||||||
|
void iterateResource_data();
|
||||||
|
void iterateResource();
|
||||||
|
void stopLinkLoop();
|
||||||
|
#ifdef QT_BUILD_INTERNAL
|
||||||
|
void engineWithNoIterator();
|
||||||
|
void testQFsFileEngineIterator_data() { iterateRelativeDirectory_data(); }
|
||||||
|
void testQFsFileEngineIterator();
|
||||||
|
#endif
|
||||||
|
void absoluteFilePathsFromRelativeIteratorPath();
|
||||||
|
void recurseWithFilters() const;
|
||||||
|
void longPath();
|
||||||
|
void dirorder();
|
||||||
|
void relativePaths();
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
void uncPaths_data();
|
||||||
|
void uncPaths();
|
||||||
|
#endif
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
void hiddenDirs_hiddenFiles();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTemporaryDir m_dataDir;
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QDirIterator::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()) {
|
||||||
|
QString destination = testdata_dir + QLatin1Char('/')
|
||||||
|
+ fileInfo.filePath().mid(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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
testdata_dir += QStringLiteral("/entrylist");
|
||||||
|
#elif defined(BUILTIN_TESTDATA)
|
||||||
|
m_dataDir = QEXTRACTTESTDATA("/testdata");
|
||||||
|
QVERIFY2(!m_dataDir.isNull(), qPrintable("Could not extract test data"));
|
||||||
|
QString testdata_dir = m_dataDir->path();
|
||||||
|
#else
|
||||||
|
|
||||||
|
// chdir into testdata directory, then find testdata by relative paths.
|
||||||
|
QString testdata_dir = m_dataDir.path();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QVERIFY(!testdata_dir.isEmpty());
|
||||||
|
// Must call QDir::setCurrent() here because all the tests that use relative
|
||||||
|
// paths depend on that.
|
||||||
|
QVERIFY2(QDir::setCurrent(testdata_dir), qPrintable("Could not chdir to " + testdata_dir));
|
||||||
|
|
||||||
|
createDirectory("entrylist");
|
||||||
|
createDirectory("entrylist/directory");
|
||||||
|
createFile("entrylist/file");
|
||||||
|
createFile("entrylist/writable");
|
||||||
|
createFile("entrylist/directory/dummy");
|
||||||
|
|
||||||
|
createDirectory("recursiveDirs");
|
||||||
|
createDirectory("recursiveDirs/dir1");
|
||||||
|
createFile("recursiveDirs/textFileA.txt");
|
||||||
|
createFile("recursiveDirs/dir1/aPage.html");
|
||||||
|
createFile("recursiveDirs/dir1/textFileB.txt");
|
||||||
|
|
||||||
|
createDirectory("foo");
|
||||||
|
createDirectory("foo/bar");
|
||||||
|
createFile("foo/bar/readme.txt");
|
||||||
|
|
||||||
|
createDirectory("empty");
|
||||||
|
|
||||||
|
#ifndef Q_NO_SYMLINKS
|
||||||
|
# if defined(Q_OS_WIN)
|
||||||
|
// ### Sadly, this is a platform difference right now.
|
||||||
|
createLink("entrylist/file", "entrylist/linktofile.lnk");
|
||||||
|
# ifndef Q_NO_SYMLINKS_TO_DIRS
|
||||||
|
createLink("entrylist/directory", "entrylist/linktodirectory.lnk");
|
||||||
|
# endif
|
||||||
|
createLink("entrylist/nothing", "entrylist/brokenlink.lnk");
|
||||||
|
# else
|
||||||
|
createLink("file", "entrylist/linktofile.lnk");
|
||||||
|
# ifndef Q_NO_SYMLINKS_TO_DIRS
|
||||||
|
createLink("directory", "entrylist/linktodirectory.lnk");
|
||||||
|
# endif
|
||||||
|
createLink("nothing", "entrylist/brokenlink.lnk");
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(Q_OS_WIN)
|
||||||
|
createDirectory("hiddenDirs_hiddenFiles");
|
||||||
|
createFile("hiddenDirs_hiddenFiles/normalFile");
|
||||||
|
createFile("hiddenDirs_hiddenFiles/.hiddenFile");
|
||||||
|
createDirectory("hiddenDirs_hiddenFiles/normalDirectory");
|
||||||
|
createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory");
|
||||||
|
createFile("hiddenDirs_hiddenFiles/normalDirectory/normalFile");
|
||||||
|
createFile("hiddenDirs_hiddenFiles/normalDirectory/.hiddenFile");
|
||||||
|
createFile("hiddenDirs_hiddenFiles/.hiddenDirectory/normalFile");
|
||||||
|
createFile("hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenFile");
|
||||||
|
createDirectory("hiddenDirs_hiddenFiles/normalDirectory/normalDirectory");
|
||||||
|
createDirectory("hiddenDirs_hiddenFiles/normalDirectory/.hiddenDirectory");
|
||||||
|
createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory/normalDirectory");
|
||||||
|
createDirectory("hiddenDirs_hiddenFiles/.hiddenDirectory/.hiddenDirectory");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDirIterator::iterateRelativeDirectory_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("dirName"); // relative from current path or abs
|
||||||
|
QTest::addColumn<QDirIterator::IteratorFlags>("flags");
|
||||||
|
QTest::addColumn<QDir::Filters>("filters");
|
||||||
|
QTest::addColumn<QStringList>("nameFilters");
|
||||||
|
QTest::addColumn<QStringList>("entries");
|
||||||
|
|
||||||
|
QTest::newRow("no flags")
|
||||||
|
<< QString("entrylist") << QDirIterator::IteratorFlags{}
|
||||||
|
<< QDir::Filters(QDir::NoFilter) << QStringList("*")
|
||||||
|
<< QString(
|
||||||
|
"entrylist/.,"
|
||||||
|
"entrylist/..,"
|
||||||
|
"entrylist/file,"
|
||||||
|
#ifndef Q_NO_SYMLINKS
|
||||||
|
"entrylist/linktofile.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/directory,"
|
||||||
|
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
|
||||||
|
"entrylist/linktodirectory.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/writable").split(',');
|
||||||
|
|
||||||
|
QTest::newRow("NoDot")
|
||||||
|
<< QString("entrylist") << QDirIterator::IteratorFlags{}
|
||||||
|
<< QDir::Filters(QDir::AllEntries | QDir::NoDot) << QStringList("*")
|
||||||
|
<< QString(
|
||||||
|
"entrylist/..,"
|
||||||
|
"entrylist/file,"
|
||||||
|
#ifndef Q_NO_SYMLINKS
|
||||||
|
"entrylist/linktofile.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/directory,"
|
||||||
|
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
|
||||||
|
"entrylist/linktodirectory.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/writable").split(',');
|
||||||
|
|
||||||
|
QTest::newRow("NoDotDot")
|
||||||
|
<< QString("entrylist") << QDirIterator::IteratorFlags{}
|
||||||
|
<< QDir::Filters(QDir::AllEntries | QDir::NoDotDot) << QStringList("*")
|
||||||
|
<< QString(
|
||||||
|
"entrylist/.,"
|
||||||
|
"entrylist/file,"
|
||||||
|
#ifndef Q_NO_SYMLINKS
|
||||||
|
"entrylist/linktofile.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/directory,"
|
||||||
|
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
|
||||||
|
"entrylist/linktodirectory.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/writable").split(',');
|
||||||
|
|
||||||
|
QTest::newRow("NoDotAndDotDot")
|
||||||
|
<< QString("entrylist") << QDirIterator::IteratorFlags{}
|
||||||
|
<< QDir::Filters(QDir::AllEntries | QDir::NoDotAndDotDot) << QStringList("*")
|
||||||
|
<< QString(
|
||||||
|
"entrylist/file,"
|
||||||
|
#ifndef Q_NO_SYMLINKS
|
||||||
|
"entrylist/linktofile.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/directory,"
|
||||||
|
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
|
||||||
|
"entrylist/linktodirectory.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/writable").split(',');
|
||||||
|
|
||||||
|
QTest::newRow("QDir::Subdirectories | QDir::FollowSymlinks")
|
||||||
|
<< QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories | QDirIterator::FollowSymlinks)
|
||||||
|
<< QDir::Filters(QDir::NoFilter) << QStringList("*")
|
||||||
|
<< QString(
|
||||||
|
"entrylist/.,"
|
||||||
|
"entrylist/..,"
|
||||||
|
"entrylist/directory/.,"
|
||||||
|
"entrylist/directory/..,"
|
||||||
|
"entrylist/file,"
|
||||||
|
#ifndef Q_NO_SYMLINKS
|
||||||
|
"entrylist/linktofile.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/directory,"
|
||||||
|
"entrylist/directory/dummy,"
|
||||||
|
#if !defined(Q_NO_SYMLINKS) && !defined(Q_NO_SYMLINKS_TO_DIRS)
|
||||||
|
"entrylist/linktodirectory.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/writable").split(',');
|
||||||
|
|
||||||
|
QTest::newRow("QDir::Subdirectories / QDir::Files")
|
||||||
|
<< QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories)
|
||||||
|
<< QDir::Filters(QDir::Files) << QStringList("*")
|
||||||
|
<< QString("entrylist/directory/dummy,"
|
||||||
|
"entrylist/file,"
|
||||||
|
#ifndef Q_NO_SYMLINKS
|
||||||
|
"entrylist/linktofile.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/writable").split(',');
|
||||||
|
|
||||||
|
QTest::newRow("QDir::Subdirectories | QDir::FollowSymlinks / QDir::Files")
|
||||||
|
<< QString("entrylist") << QDirIterator::IteratorFlags(QDirIterator::Subdirectories | QDirIterator::FollowSymlinks)
|
||||||
|
<< QDir::Filters(QDir::Files) << QStringList("*")
|
||||||
|
<< QString("entrylist/file,"
|
||||||
|
#ifndef Q_NO_SYMLINKS
|
||||||
|
"entrylist/linktofile.lnk,"
|
||||||
|
#endif
|
||||||
|
"entrylist/directory/dummy,"
|
||||||
|
"entrylist/writable").split(',');
|
||||||
|
|
||||||
|
QTest::newRow("empty, default")
|
||||||
|
<< QString("empty") << QDirIterator::IteratorFlags{}
|
||||||
|
<< QDir::Filters(QDir::NoFilter) << QStringList("*")
|
||||||
|
<< QString("empty/.,empty/..").split(',');
|
||||||
|
|
||||||
|
QTest::newRow("empty, QDir::NoDotAndDotDot")
|
||||||
|
<< QString("empty") << QDirIterator::IteratorFlags{}
|
||||||
|
<< QDir::Filters(QDir::NoDotAndDotDot) << QStringList("*")
|
||||||
|
<< QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDirIterator::iterateRelativeDirectory()
|
||||||
|
{
|
||||||
|
QFETCH(QString, dirName);
|
||||||
|
QFETCH(QDirIterator::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());
|
||||||
|
|
||||||
|
// Using canonical file paths for final comparison
|
||||||
|
list << info.canonicalFilePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The order of items returned by QDirIterator is not guaranteed.
|
||||||
|
list.sort();
|
||||||
|
|
||||||
|
QStringList sortedEntries;
|
||||||
|
for (const QString &item : entries)
|
||||||
|
sortedEntries.append(QFileInfo(item).canonicalFilePath());
|
||||||
|
sortedEntries.sort();
|
||||||
|
|
||||||
|
if (sortedEntries != list) {
|
||||||
|
qDebug() << "ACTUAL: " << list;
|
||||||
|
qDebug() << "EXPECTED:" << sortedEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCOMPARE(list, sortedEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDirIterator::iterateResource_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("dirName"); // relative from current path or abs
|
||||||
|
QTest::addColumn<QDirIterator::IteratorFlags>("flags");
|
||||||
|
QTest::addColumn<QDir::Filters>("filters");
|
||||||
|
QTest::addColumn<QStringList>("nameFilters");
|
||||||
|
QTest::addColumn<QStringList>("entries");
|
||||||
|
|
||||||
|
QTest::newRow("invalid") << QString::fromLatin1(":/testdata/burpaburpa") << QDirIterator::IteratorFlags{}
|
||||||
|
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
|
||||||
|
<< QStringList();
|
||||||
|
QTest::newRow("qrc:/testdata") << u":/testdata/"_s << QDirIterator::IteratorFlags{}
|
||||||
|
<< QDir::Filters(QDir::NoFilter) << QStringList(QLatin1String("*"))
|
||||||
|
<< QString::fromLatin1(":/testdata/entrylist").split(QLatin1String(","));
|
||||||
|
QTest::newRow("qrc:/testdata/entrylist") << u":/testdata/entrylist"_s << QDirIterator::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)
|
||||||
|
<< 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()
|
||||||
|
{
|
||||||
|
QFETCH(QString, dirName);
|
||||||
|
QFETCH(QDirIterator::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();
|
||||||
|
if (!dir.startsWith(":/qt-project.org"))
|
||||||
|
list << dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.sort();
|
||||||
|
QStringList sortedEntries = entries;
|
||||||
|
sortedEntries.sort();
|
||||||
|
|
||||||
|
if (sortedEntries != list) {
|
||||||
|
qDebug() << "ACTUAL:" << list;
|
||||||
|
qDebug() << "EXPECTED:" << sortedEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCOMPARE(list, sortedEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDirIterator::stopLinkLoop()
|
||||||
|
{
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
// ### Sadly, this is a platform difference right now.
|
||||||
|
createLink(QDir::currentPath() + QLatin1String("/entrylist"), "entrylist/entrylist1.lnk");
|
||||||
|
createLink("entrylist/.", "entrylist/entrylist2.lnk");
|
||||||
|
createLink("entrylist/../entrylist/.", "entrylist/entrylist3.lnk");
|
||||||
|
createLink("entrylist/..", "entrylist/entrylist4.lnk");
|
||||||
|
createLink(QDir::currentPath() + QLatin1String("/entrylist"), "entrylist/directory/entrylist1.lnk");
|
||||||
|
createLink("entrylist/.", "entrylist/directory/entrylist2.lnk");
|
||||||
|
createLink("entrylist/../directory/.", "entrylist/directory/entrylist3.lnk");
|
||||||
|
createLink("entrylist/..", "entrylist/directory/entrylist4.lnk");
|
||||||
|
#else
|
||||||
|
createLink(QDir::currentPath() + QLatin1String("/entrylist"), "entrylist/entrylist1.lnk");
|
||||||
|
createLink(".", "entrylist/entrylist2.lnk");
|
||||||
|
createLink("../entrylist/.", "entrylist/entrylist3.lnk");
|
||||||
|
createLink("..", "entrylist/entrylist4.lnk");
|
||||||
|
createLink(QDir::currentPath() + QLatin1String("/entrylist"), "entrylist/directory/entrylist1.lnk");
|
||||||
|
createLink(".", "entrylist/directory/entrylist2.lnk");
|
||||||
|
createLink("../directory/.", "entrylist/directory/entrylist3.lnk");
|
||||||
|
createLink("..", "entrylist/directory/entrylist4.lnk");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QDirIterator it(QLatin1String("entrylist"), QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
|
||||||
|
QStringList list;
|
||||||
|
int max = 200;
|
||||||
|
while (--max && it.hasNext())
|
||||||
|
it.nextFileInfo();
|
||||||
|
QVERIFY(max);
|
||||||
|
|
||||||
|
// The goal of this test is only to ensure that the test above don't malfunction
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef QT_BUILD_INTERNAL
|
||||||
|
class EngineWithNoIterator : public QFSFileEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EngineWithNoIterator(const QString &fileName)
|
||||||
|
: QFSFileEngine(fileName)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
QAbstractFileEngineIterator *beginEntryList(QDir::Filters, const QStringList &) override
|
||||||
|
{ return 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class EngineWithNoIteratorHandler : public QAbstractFileEngineHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAbstractFileEngine *create(const QString &fileName) const override
|
||||||
|
{
|
||||||
|
return new EngineWithNoIterator(fileName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef QT_BUILD_INTERNAL
|
||||||
|
void tst_QDirIterator::engineWithNoIterator()
|
||||||
|
{
|
||||||
|
EngineWithNoIteratorHandler handler;
|
||||||
|
|
||||||
|
QDir("entrylist").entryList();
|
||||||
|
QVERIFY(true); // test that the above line doesn't crash
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomEngineHandler : public QAbstractFileEngineHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QAbstractFileEngine *create(const QString &fileName) const override
|
||||||
|
{
|
||||||
|
// We want to test QFSFileEngine specifically, so force QDirIterator to use it
|
||||||
|
// over the default QFileSystemEngine
|
||||||
|
return new QFSFileEngine(fileName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_QDirIterator::testQFsFileEngineIterator()
|
||||||
|
{
|
||||||
|
QFETCH(QString, dirName);
|
||||||
|
QFETCH(QStringList, nameFilters);
|
||||||
|
QFETCH(QDir::Filters, filters);
|
||||||
|
QFETCH(QDirIterator::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"))
|
||||||
|
isEmpty = false; // At least one entry in `entrylist` dir
|
||||||
|
}
|
||||||
|
QVERIFY(!isEmpty);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void tst_QDirIterator::absoluteFilePathsFromRelativeIteratorPath()
|
||||||
|
{
|
||||||
|
QDirIterator it("entrylist/", QDir::NoDotAndDotDot);
|
||||||
|
while (it.hasNext())
|
||||||
|
QVERIFY(it.nextFileInfo().absoluteFilePath().contains("entrylist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDirIterator::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);
|
||||||
|
|
||||||
|
QVERIFY(!it.hasNext());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDirIterator::longPath()
|
||||||
|
{
|
||||||
|
QDir dir;
|
||||||
|
dir.mkdir("longpaths");
|
||||||
|
dir.cd("longpaths");
|
||||||
|
|
||||||
|
QString dirName = "x";
|
||||||
|
int 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()) {
|
||||||
|
++m;
|
||||||
|
it.nextFileInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
QCOMPARE(n, m);
|
||||||
|
|
||||||
|
dirName.chop(1);
|
||||||
|
while (dirName.size() > 0 && dir.exists(dirName) && dir.rmdir(dirName)) {
|
||||||
|
dirName.chop(1);
|
||||||
|
}
|
||||||
|
dir.cdUp();
|
||||||
|
dir.rmdir("longpaths");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDirIterator::dirorder()
|
||||||
|
{
|
||||||
|
QDirIterator iterator("foo", QDirIterator::Subdirectories);
|
||||||
|
while (iterator.hasNext() && iterator.next() != "foo/bar")
|
||||||
|
{ }
|
||||||
|
|
||||||
|
QCOMPARE(iterator.filePath(), QString("foo/bar"));
|
||||||
|
QCOMPARE(iterator.fileInfo().filePath(), QString("foo/bar"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QDirIterator::relativePaths()
|
||||||
|
{
|
||||||
|
QDirIterator iterator("*", QDirIterator::Subdirectories);
|
||||||
|
while(iterator.hasNext()) {
|
||||||
|
QCOMPARE(iterator.filePath(), QDir::cleanPath(iterator.filePath()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
void tst_QDirIterator::uncPaths_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("dirName");
|
||||||
|
QTest::newRow("uncserver")
|
||||||
|
<<QString("//" + QTest::uncServerName());
|
||||||
|
QTest::newRow("uncserver/testshare")
|
||||||
|
<<QString("//" + QTest::uncServerName() + "/testshare");
|
||||||
|
QTest::newRow("uncserver/testshare/tmp")
|
||||||
|
<<QString("//" + QTest::uncServerName() + "/testshare/tmp");
|
||||||
|
}
|
||||||
|
void tst_QDirIterator::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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef Q_OS_WIN
|
||||||
|
// In Unix it is easy to create hidden files, but in Windows it requires
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
// Only files
|
||||||
|
{
|
||||||
|
int matches = 0;
|
||||||
|
int failures = 0;
|
||||||
|
QDirIterator di("hiddenDirs_hiddenFiles", QDir::Files | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
|
||||||
|
while (di.hasNext()) {
|
||||||
|
++matches;
|
||||||
|
if (di.nextFileInfo().isDir())
|
||||||
|
++failures; // search was only supposed to find files
|
||||||
|
}
|
||||||
|
QCOMPARE(matches, 6);
|
||||||
|
QCOMPARE(failures, 0);
|
||||||
|
}
|
||||||
|
// Only directories
|
||||||
|
{
|
||||||
|
int matches = 0;
|
||||||
|
int failures = 0;
|
||||||
|
QDirIterator di("hiddenDirs_hiddenFiles", QDir::Dirs | QDir::Hidden | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
|
||||||
|
while (di.hasNext()) {
|
||||||
|
++matches;
|
||||||
|
if (!di.nextFileInfo().isDir())
|
||||||
|
++failures; // search was only supposed to find files
|
||||||
|
}
|
||||||
|
QCOMPARE(matches, 6);
|
||||||
|
QCOMPARE(failures, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_QDirIterator)
|
||||||
|
|
||||||
|
#include "tst_qdiriterator.moc"
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user