Revision control

++ // 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
++ // Qt-Security score:significant reason:default
++
++ /*!
++ \since 6.8
++ \class QDirListing
++ \inmodule QtCore
++ \ingroup io
++ \brief The QDirListing class provides an STL-style iterator for directory entries.
++
++ 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(), QDirListing does not support sorting.
++
++ The QDirListing constructor takes a directory path string as
++ argument. Here's how to iterate over all entries recursively:
++
++ \snippet code/src_corelib_io_qdirlisting.cpp 0
++
++ Here's how to find and read all regular files filtered by name, recursively:
++
++ \snippet code/src_corelib_io_qdirlisting.cpp 1
++
++ Here's how to list only regular files, recursively:
++ \snippet code/src_corelib_io_qdirlisting.cpp 5
++
++ Here's how to list only regular files and symbolic links to regular
++ files, recursively:
++ \snippet code/src_corelib_io_qdirlisting.cpp 6
++
++ //! [std-input-iterator-tag]
++ QDirListing::const_iterator models C++20
++ that is, it is a move-only, forward-only, single-pass iterator, that
++ doesn't allow random access.
++ //! [std-input-iterator-tag]
++ It can be used in ranged-for loops (or with C++20 range algorithms that don't
++ require random access iterators). Dereferencing a valid iterator returns
++ a QDirListing::DirEntry object. The (c)end() sentinel marks the end of
++ the iteration. Dereferencing an iterator that is equal to \l{sentinel} is
++ undefined behavior.
++
++ 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 QDirListing::IteratorFlag
++
++ This enum class describes flags that can be used to configure the behavior
++ of QDirListing. Values from this enumerator can be bitwise OR'ed together.
++
++ \value Default
++ List all entries, that is, files, directories, symbolic links including broken
++ symbolic links (where the target doesn't exist) and special (\e other) system
++ files, see ExcludeOther for details.
++ Hidden files and directories and the special entries \c{.} and \c{..}
++ aren't listed by default.
++
++ \value ExcludeFiles
++ Don't list regular files. When combined with ResolveSymlinks, symbolic
++ links to regular files will be excluded too.
++
++ \value ExcludeDirs
++ Don't list directories. When combined with ResolveSymlinks, symbolic
++ links to directories will be excluded too.
++
++ \omitvalue ExcludeSpecial
++ \value [since 6.10] ExcludeOther
++ Don't list file system entries that are \e not directories, regular files,
++ or symbolic links.
++ \list
++ \li On Unix, a special (other) file system entry is a FIFO, socket,
++ character device, or block device. For more details see the
++ manual page.
++ \li On Windows (for historical reasons) \c .lnk files are considered
++ special (other) file system entries.
++ \endlist
++
++ \value ResolveSymlinks
++ Filter symbolic links based on the type of the target of the link,
++ rather than the symbolic link itself. Broken symbolic links (where
++ the target doesn't exist) are excluded, set IncludeBrokenSymlinks
++ to include them.
++ This flag is ignored on operating systems that don't support symbolic links.
++
++ \value [since 6.11] IncludeBrokenSymlinks
++ Lists broken symbolic links, where the target doesn't exist, regardless
++ of the status of the ResolveSymlinks flag.
++ This flag is ignored on operating systems that don't support symbolic links.
++
++ \value FilesOnly
++ Only regular files will be listed. When combined with ResolveSymlinks,
++ symbolic links to files will also be listed.
++
++ \value DirsOnly
++ Only directories will be listed. When combined with ResolveSymlinks,
++ symbolic links to directories will also be listed.
++
++ \value IncludeHidden
++ List hidden entries. When combined with Recursive, the iteration will
++ recurse into hidden sub-directories as well.
++
++ \value IncludeDotAndDotDot
++ List the \c {.} and \c{..} special entries.
++
++ \value CaseSensitive
++ The file glob patterns in the name filters passed to the QDirListing
++ constructor, will be matched case sensitively (for details, see
++ QDir::setNameFilters()).
++
++ \value Recursive
++ List entries inside all sub-directories as well. When combined with
++ FollowDirSymlinks, symbolic links to directories will be iterated too.
++
++ \value FollowDirSymlinks
++ When combined with Recursive, symbolic links to directories will be
++ iterated too. Symbolic link loops (e.g., link => . or link => ..) are
++ automatically detected and ignored.
++
++ \omitvalue NoNameFiltersForDirs
++ */
++
++ #include "qdirlisting.h"
++ #include "qdirentryinfo_p.h"
++
++ #include "qdir_p.h"
++ #include "qdiriterator.h"
++ #include "qabstractfileengine_p.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 <variant>
++ #include <vector>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace Qt::StringLiterals;
++
++ class QDirListingPrivate
++ {
++ Q_DISABLE_COPY_MOVE(QDirListingPrivate)
++ public:
++ QDirListingPrivate() = default;
++
++ // the default for std::stack is std::deque, but std::vector is more apt:
++ template <typename T>
++ struct vector_stack : std::stack<T, std::vector<T>>
++ {
++ using Base = std::stack<T, std::vector<T>>;
++ using Base::Base;
++ void clear() { this->c.clear(); } // std::stack is also missing clear()
++ };
++
++ void init();
++ void advance();
++ void beginIterating();
++
++ bool entryMatches(QDirEntryInfo &info);
++ void pushDirectory(QDirEntryInfo &info);
++ void pushInitialDirectory();
++
++ void checkAndPushDirectory(QDirEntryInfo &info);
++ bool matchesFilters(QDirEntryInfo &data) const;
++ bool hasIterators() const;
++
++ QDirEntryInfo initialEntryInfo;
++ QStringList nameFilters;
++ QDirListing::IteratorFlags iteratorFlags;
++ QDirEntryInfo currentEntryInfo;
++
++ #if QT_CONFIG(regularexpression)
++ std::vector<QRegularExpression> nameRegExps;
++ bool regexMatchesName(const QString &fileName) const
++ {
++ if (nameRegExps.empty())
++ return true;
++ auto hasMatch = [&fileName](const auto &re) { return re.match(fileName).hasMatch(); };
++ return std::any_of(nameRegExps.cbegin(), nameRegExps.cend(), hasMatch);
++ }
++ #endif
++
++ // QDirListing scans either through a QAbstractFileEngine (resources, custom
++ // engines) or natively (QFileSystemEngine + QFileSystemIterator). The mode
++ // is selected by beginIterating().
++ struct EngineData {
++ using FEngineIteratorPtr = std::unique_ptr<QAbstractFileEngineIterator>;
++ EngineData(std::unique_ptr<QAbstractFileEngine> engine) noexcept : engine(std::move(engine)) {}
++ std::unique_ptr<QAbstractFileEngine> engine;
++ vector_stack<FEngineIteratorPtr> iterators;
++ QDuplicateTracker<QString> visitedLinks;
++ };
++ struct NativeData {
++ #ifndef QT_NO_FILESYSTEMITERATOR
++ using FsIteratorPtr = std::unique_ptr<QFileSystemIterator>;
++ vector_stack<FsIteratorPtr> iterators;
++ QDuplicateTracker<QFileSystemNativeId> visitedLinks;
++ #else
++ NativeData() noexcept
++ { qWarning("Qt was built with -no-feature-filesystemiterator: no files/plugins will be found!"); }
++ #endif
++ };
++ std::variant<std::monostate, EngineData, NativeData> data;
++
++ private:
++ bool matchesFilters(const QFileInfo &fileInfo) const;
++ bool matchesFilters(QDirEntryInfo::Iterator &iterator) const;
++ bool matchesFilters(const QFileInfo &fileInfo, const QString &fileName) const;
++ bool matchesFilters(QDirEntryInfo::Native &native) const;
++ };
++
++ void QDirListingPrivate::init()
++ {
++ if (nameFilters.contains("*"_L1))
++ nameFilters.clear();
++
++ #if QT_CONFIG(regularexpression)
++ nameRegExps.reserve(size_t(nameFilters.size()));
++
++ const bool isCase = iteratorFlags.testAnyFlags(QDirListing::IteratorFlag::CaseSensitive);
++ const auto cs = isCase ? Qt::CaseSensitive : Qt::CaseInsensitive;
++ for (const auto &filter : std::as_const(nameFilters))
++ nameRegExps.emplace_back(QRegularExpression::fromWildcard(filter, cs));
++ #endif
++ }
++
++ /*!
++ \internal
++
++ Resets the iteration state (if any), so that calling begin()/cbegin()
++ always starts iterating anew.
++ */
++ void QDirListingPrivate::beginIterating()
++ {
++ Q_ASSERT(std::holds_alternative<QDirEntryInfo::Native>(initialEntryInfo.content));
++ QDirEntryInfo::Native &native = *std::get_if<QDirEntryInfo::Native>(&initialEntryInfo.content);
++
++ // (Re-)select our operating mode. Note how std::variant::emplace will
++ // recreate the object even if it matches the current type, which is
++ // necessary for QDuplicateTracker.
++ if (auto engine = QFileSystemEngine::createLegacyEngine(native.entry, native.metaData))
++ data.emplace<EngineData>(std::move(engine));
++ else
++ data.emplace<NativeData>();
++
++ pushDirectory(initialEntryInfo);
++ }
++
++ void QDirListingPrivate::pushDirectory(QDirEntryInfo &entryInfo)
++ {
++ const bool followSymlinks =
++ iteratorFlags.testAnyFlags(QDirListing::IteratorFlag::FollowDirSymlinks);
++
++ if (auto *d = std::get_if<EngineData>(&data)) {
++ const QString path = [&entryInfo] {
++ #ifdef Q_OS_WIN
++ if (entryInfo.isSymLink())
++ return entryInfo.canonicalFilePath();
++ #endif
++ return entryInfo.filePath();
++ }();
++
++ // Stop link loops
++ if (followSymlinks && d->visitedLinks.hasSeen(entryInfo.canonicalFilePath()))
++ return;
++
++ #ifdef Q_OS_WIN
++ if (entryInfo.isJunction() && d->visitedLinks.hasSeen(entryInfo.junctionTarget()))
++ return;
++ #endif
++
++ d->engine->setFileName(path);
++ if (auto it = d->engine->beginEntryList(path, iteratorFlags, nameFilters)) {
++ d->iterators.push(std::move(it));
++ } else {
++ // No iterator; no entry list.
++ }
++ } else if (auto *d = std::get_if<NativeData>(&data); true) {
++ Q_ASSERT(d);
++ #ifndef QT_NO_FILESYSTEMITERATOR
++ // In native mode, entryInfo is always Native: the initial entry is
++ // (see beginIterating()) and QFileSystemIterator::advance() only ever
++ // produces Native entries.
++ Q_ASSERT(std::holds_alternative<QDirEntryInfo::Native>(entryInfo.content));
++ const auto native = std::get_if<QDirEntryInfo::Native>(&entryInfo.content);
++ auto it = std::make_unique<QFileSystemIterator>(native->entry, iteratorFlags);
++
++ // Stop link loops
++ if (followSymlinks) {
++ QFileSystemNativeId id = it->nativeId();
++ if (id.isValid() && d->visitedLinks.hasSeen(id))
++ return;
++ }
++
++ #ifdef Q_OS_WIN
++ if (entryInfo.isJunction()) {
++ QFileSystemNativeId id = it->nativeId();
++ if (id.isValid() && d->visitedLinks.hasSeen(id))
++ return;
++ }
++ #endif
++
++ d->iterators.push(std::move(it));
++ #endif
++ }
++ }
++
++ bool QDirListingPrivate::entryMatches(QDirEntryInfo &entryInfo)
++ {
++ checkAndPushDirectory(entryInfo);
++ return matchesFilters(entryInfo);
++ }
++
++ /*!
++ \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). The iterators are stored in a
++ stack.
++
++ A typical example of doing recursive iteration:
++ - while iterating directory A we find a sub-dir B
++ - an iterator for B is pushed to the stack
++ - B's iterator is processed (stack.top()) first; then the loop
++ goes back to processing A's iterator
++ */
++ void QDirListingPrivate::advance()
++ {
++ if (auto *d = std::get_if<EngineData>(&data)) {
++ while (!d->iterators.empty()) {
++ // Find the next valid iterator that matches the filters.
++ // Always use top() because entryMatches() may modify `iterators`!
++ while (d->iterators.top()->advance()) {
++ QDirEntryInfo entryInfo{d->iterators.top().get()};
++ if (entryMatches(entryInfo)) {
++ currentEntryInfo = std::move(entryInfo);
++ return;
++ }
++ }
++
++ d->iterators.pop();
++ }
++ } else if (auto *d = std::get_if<NativeData>(&data); true) {
++ Q_ASSERT(d);
++ #ifndef QT_NO_FILESYSTEMITERATOR
++ while (!d->iterators.empty()) {
++ // Find the next valid iterator that matches the filters.
++ // Always use top() because entryMatches() may modify `iterators`!
++ while (std::optional r = d->iterators.top()->advance()) {
++ if (entryMatches(*r)) {
++ currentEntryInfo = std::move(*r);
++ return;
++ }
++ }
++
++ d->iterators.pop();
++ }
++ #endif
++ }
++ }
++
++ static bool isDotOrDotDot(QStringView fileName)
++ {
++ return fileName == "."_L1 || fileName == ".."_L1;
++ }
++
++ void QDirListingPrivate::checkAndPushDirectory(QDirEntryInfo &entryInfo)
++ {
++ using F = QDirListing::IteratorFlag;
++ // If we're doing flat iteration, we're done.
++ if (!iteratorFlags.testAnyFlags(F::Recursive))
++ return;
++
++ // Follow symlinks only when asked
++ if (!iteratorFlags.testAnyFlags(F::FollowDirSymlinks) && entryInfo.isSymLink())
++ return;
++
++ // Never follow . and ..
++ if (isDotOrDotDot(entryInfo.fileName()))
++ return;
++
++ // No hidden directories unless requested
++ const bool includeHidden = iteratorFlags.testAnyFlags(QDirListing::IteratorFlag::IncludeHidden);
++ if (!includeHidden && entryInfo.isHidden())
++ return;
++
++ // Never follow non-directory entries
++ if (!entryInfo.isDir())
++ return;
++
++ pushDirectory(entryInfo);
++ }
++
++ bool QDirListingPrivate::matchesFilters(QDirEntryInfo::Native &native) const
++ {
++ using F = QDirListing::IteratorFlag;
++ using M = QFileSystemMetaData;
++
++ const QString fileName = native.entry.fileName();
++ if (fileName.isEmpty())
++ return false;
++
++ // name filter
++ #if QT_CONFIG(regularexpression)
++ const bool skipNameFilters = iteratorFlags.testAnyFlags(F::NoNameFiltersForDirs)
++ && native.ensureFilled(M::DirectoryType).isDirectory();
++ if (!skipNameFilters) {
++ if (!regexMatchesName(fileName))
++ return false;
++ }
++ #endif // QT_CONFIG(regularexpression)
++
++ if (isDotOrDotDot(fileName))
++ return iteratorFlags.testFlags(F::IncludeDotAndDotDot);
++
++ if (!iteratorFlags.testAnyFlag(F::IncludeHidden)
++ && native.ensureFilled(M::HiddenAttribute).isHidden()) {
++ return false;
++ }
++
++ if (iteratorFlags.testAnyFlags(F::IncludeBrokenSymlinks)
++ && native.ensureFilled(M::LegacyLinkType).isLegacyLink()
++ && !native.ensureFilled(M::ExistsAttribute).exists()) {
++ return true;
++ }
++
++ if (iteratorFlags.testFlag(F::ResolveSymlinks)) {
++ if (native.ensureFilled(M::LegacyLinkType).isLegacyLink()
++ && !native.ensureFilled(M::ExistsAttribute).exists()) {
++ return false; // Exclude broken symlinks; anything else will be filtered below
++ }
++ } else {
++ constexpr auto f = F::ExcludeFiles | F::ExcludeDirs | F::ExcludeOther;
++ const bool filterByTargetType = iteratorFlags.testAnyFlags(f);
++ if (filterByTargetType && native.ensureFilled(M::LegacyLinkType).isLegacyLink())
++ return false;
++ }
++
++ if (iteratorFlags.testAnyFlag(F::ExcludeOther)
++ && !native.ensureFilled(M::FileType).isFile()
++ && !native.ensureFilled(M::DirectoryType).isDirectory()
++ && !native.ensureFilled(M::LegacyLinkType).isLegacyLink()) {
++ return false;
++ }
++
++ if (iteratorFlags.testAnyFlags(F::ExcludeDirs)
++ && native.ensureFilled(M::DirectoryType).isDirectory()) {
++ return false;
++ }
++
++ if (iteratorFlags.testAnyFlags(F::ExcludeFiles) && native.ensureFilled(M::FileType).isFile())
++ return false;
++
++ return true;
++ }
++
++ bool QDirListingPrivate::matchesFilters(const QFileInfo &fileInfo, const QString &fileName) const
++ {
++ using F = QDirListing::IteratorFlag;
++
++ Q_ASSERT(!fileName.isEmpty());
++
++ // name filter
++ #if QT_CONFIG(regularexpression)
++ const bool skipNameFilters = iteratorFlags.testFlag(F::NoNameFiltersForDirs)
++ && fileInfo.isDir();
++ if (!skipNameFilters) {
++ if (!regexMatchesName(fileName))
++ return false;
++ }
++ #endif // QT_CONFIG(regularexpression)
++
++ if (isDotOrDotDot(fileName))
++ return iteratorFlags.testFlag(F::IncludeDotAndDotDot);
++
++ if (!iteratorFlags.testFlag(F::IncludeHidden) && fileInfo.isHidden())
++ return false;
++
++ const bool includeBrokenSymlinks = iteratorFlags.testAnyFlags(F::IncludeBrokenSymlinks);
++ if (includeBrokenSymlinks && fileInfo.isSymLink() && !fileInfo.exists())
++ return true;
++
++ if (iteratorFlags.testFlag(F::ResolveSymlinks)) {
++ if (fileInfo.isSymLink() && !fileInfo.exists())
++ return false; // Exclude broken symlinks; anything else will be filtered below
++ } else {
++ constexpr auto f = F::ExcludeFiles | F::ExcludeDirs | F::ExcludeOther;
++ const bool filterByTargetType = iteratorFlags.testAnyFlags(f);
++ if (filterByTargetType && fileInfo.isSymLink())
++ return false;
++ }
++
++ if (iteratorFlags.testFlag(F::ExcludeOther)
++ && !fileInfo.isFile() && !fileInfo.isDir() && !fileInfo.isSymLink()) {
++ return false;
++ }
++
++ if (iteratorFlags.testFlag(F::ExcludeDirs) && fileInfo.isDir())
++ return false;
++
++ if (iteratorFlags.testFlag(F::ExcludeFiles) && fileInfo.isFile())
++ return false;
++
++ return true;
++ }
++
++ bool QDirListingPrivate::matchesFilters(QDirEntryInfo::Iterator &iterator) const
++ {
++ using F = QDirListing::IteratorFlag;
++
++ const QString fileName = iterator.iterator->currentFileName();
++ if (fileName.isEmpty())
++ return false;
++
++ // Fast path that doesn't require conversion to QFileInfo
++ if (iteratorFlags.testFlag(F::IncludeHidden)
++ && !iteratorFlags.testAnyFlags(
++ F::NoNameFiltersForDirs |
++ F::IncludeBrokenSymlinks |
++ F::ResolveSymlinks |
++ F::ExcludeFiles |
++ F::ExcludeDirs |
++ F::ExcludeOther)) {
++
++ #if QT_CONFIG(regularexpression)
++ if (!regexMatchesName(fileName))
++ return false;
++ #endif
++ if (isDotOrDotDot(fileName))
++ return iteratorFlags.testFlags(F::IncludeDotAndDotDot);
++
++ return true;
++ }
++
++ // Conversion to QFileInfo can be expensife, but now we need to do it.
++ return matchesFilters(iterator.ensureFileInfo(), fileName);
++ }
++
++ bool QDirListingPrivate::matchesFilters(const QFileInfo &fileInfo) const
++ {
++ const QString fileName = fileInfo.fileName();
++ return !fileName.isEmpty() && matchesFilters(fileInfo, fileName);
++ }
++
++ /*!
++ \internal
++
++ This function returns \c true if the current entry matches the filters
++ (i.e., the current entry will be returned as part of the directory
++ iteration); otherwise, \c false is returned.
++ */
++ bool QDirListingPrivate::matchesFilters(QDirEntryInfo &entryInfo) const
++ {
++ return std::visit([this](auto &e) { return matchesFilters(e); },
++ entryInfo.content);
++ }
++
++ bool QDirListingPrivate::hasIterators() const
++ {
++ if (auto *d = std::get_if<EngineData>(&data))
++ return !d->iterators.empty();
++
++ #if !defined(QT_NO_FILESYSTEMITERATOR)
++ if (auto *d = std::get_if<NativeData>(&data))
++ return !d->iterators.empty();
++ #endif
++
++ return false;
++ }
++
++ /*!
++ Constructs a QDirListing that can iterate over \a path.
++
++ You can pass options via \a flags to control how the directory should
++ be iterated.
++
++ By default, \a flags is IteratorFlag::Default.
++
++ \sa IteratorFlags
++ */
++ QDirListing::QDirListing(const QString &path, IteratorFlags flags)
++ : d(new QDirListingPrivate)
++ {
++ d->initialEntryInfo.content = QDirEntryInfo::Native { QFileSystemEntry(path), {} };
++ d->iteratorFlags = flags;
++ d->init();
++ }
++
++ /*!
++ Constructs a QDirListing that can iterate over \a path.
++
++ You can pass options via \a flags to control how the directory should
++ be iterated. By default, \a flags is IteratorFlag::Default.
++
++ The listed entries will be filtered according to the file glob patterns
++ in \a nameFilters, which are converted to a regular expression using
++ QRegularExpression::fromWildcard (see QDir::setNameFilters() for more
++ details).
++
++ For example, the following iterator could be used to iterate over audio
++ files:
++
++ \snippet code/src_corelib_io_qdirlisting.cpp 2
++
++ Sometimes you can filter by name more efficiently by iterating over the
++ entries with a range-for loop, using string comparison. For example:
++
++ \snippet code/src_corelib_io_qdirlisting.cpp 7
++
++ \sa IteratorFlags, QDir::setNameFilters()
++ */
++ QDirListing::QDirListing(const QString &path, const QStringList &nameFilters, IteratorFlags flags)
++ : d(new QDirListingPrivate)
++ {
++ d->initialEntryInfo.content = QDirEntryInfo::Native { QFileSystemEntry(path), {} };
++ d->nameFilters = nameFilters;
++ d->iteratorFlags = flags;
++ d->init();
++ }
++
++ /*!
++ \fn QDirListing::QDirListing(QDirListing &&other)
++
++ Move constructor. Moves \a other into this QDirListing.
++
++ //! [partially-formed]
++ \note The moved-from object \a other is placed in a partially-formed state,
++ in which the only valid operations are destruction and assignment of a new
++ value.
++ //! [partially-formed]
++ */
++
++ /*!
++ \fn QDirListing &QDirListing::operator=(QDirListing &&other)
++
++ Move-assigns \a other to this QDirListing.
++
++ \include qdirlisting.cpp partially-formed
++ */
++
++ /*!
++ Destroys the QDirListing.
++ */
++ QDirListing::~QDirListing()
++ {
++ delete d;
++ }
++
++ /*!
++ Returns the directory path used to construct this QDirListing.
++ */
++ QString QDirListing::iteratorPath() const
++ {
++ return d->initialEntryInfo.filePath();
++ }
++
++ /*!
++ Returns the set of IteratorFlags used to construct this QDirListing.
++ */
++ QDirListing::IteratorFlags QDirListing::iteratorFlags() const
++ {
++ return d->iteratorFlags;
++ }
++
++ /*!
++ Returns the list of file name glob filters used to construct this
++ QDirListing.
++ */
++ QStringList QDirListing::nameFilters() const
++ {
++ return d->nameFilters;
++ }
++
++ /*!
++ \class QDirListing::const_iterator
++ \since 6.8
++ \inmodule QtCore
++ \ingroup io
++
++ The iterator type returned by QDirListing::cbegin().
++
++ //! [dirlisting-iterator-behavior]
++ \list
++ \li This is a forward-only, single-pass iterator (you cannot iterate
++ directory entries in reverse order)
++ \li Can't be copied, only \c{std::move()}d.
++ \li \include qdirlisting.cpp post-increment-partially-formed
++ \li Doesn't allow random access
++ \li Can be used in ranged-for loops; or with C++20 std::ranges algorithms
++ that don't require random access iterators
++ \li Dereferencing a valid iterator returns a \c{const DirEntry &}
++ \li (c)end() returns a \l QDirListing::sentinel that signals the end of
++ the iteration. Dereferencing an iterator that compares equal to end()
++ is undefined behavior
++ \endlist
++ //! [dirlisting-iterator-behavior]
++
++ \include qdirlisting.cpp ranges-algorithms-note
++
++ \sa QDirListing, QDirListing::sentinel, QDirListing::DirEntry
++ */
++
++ /*!
++ \typealias QDirListing::const_iterator::reference
++
++ A typedef for \c {const QDirListing::DirEntry &}.
++ */
++
++ /*!
++ \typealias QDirListing::const_iterator::pointer
++
++ A typedef for \c {const QDirListing::DirEntry *}.
++ */
++
++ /*!
++ \class QDirListing::sentinel
++ \since 6.8
++ \inmodule QtCore
++ \ingroup io
++
++ \l QDirListing returns an object of this type to signal the end of
++ iteration. Dereferencing a \l QDirListing::const_iterator that is
++ equal to \c sentinel{} is undefined behavior.
++
++ \include qdirlisting.cpp ranges-algorithms-note
++
++ \sa QDirListing, QDirListing::const_iterator, QDirListing::DirEntry
++ */
++
++ /*!
++ \fn QDirListing::const_iterator QDirListing::begin() const
++ \fn QDirListing::const_iterator QDirListing::cbegin() const
++ \fn QDirListing::sentinel QDirListing::end() const
++ \fn QDirListing::sentinel QDirListing::cend() const
++
++ (c)begin() returns a QDirListing::const_iterator that can be used to
++ iterate over directory entries.
++
++ \include qdirlisting.cpp dirlisting-iterator-behavior
++
++ \note Each time (c)begin() is called on the same QDirListing object,
++ the internal state is reset and the iteration starts anew.
++
++ (Some of the above restrictions are dictated by the underlying system
++ library functions' implementation).
++
++ 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
++
++ //! [ranges-algorithms-note]
++ \note The "classical" STL algorithms don't support iterator/sentinel, so
++ you need to use C++20 std::ranges algorithms for QDirListing, or else a
++ 3rd-party library that provides range-based algorithms in C++17.
++ //! [ranges-algorithms-note]
++
++ \sa QDirListing::DirEntry
++ */
++ QDirListing::const_iterator QDirListing::begin() const
++ {
++ d->beginIterating();
++ const_iterator it{d};
++ ++it;
++ return it;
++ }
++
++ /*!
++ \fn const QDirListing::DirEntry &QDirListing::const_iterator::operator*() const
++
++ Returns a \c{const QDirListing::DirEntry &} of the directory entry this
++ iterator points to.
++ */
++
++ /*!
++ \fn const QDirListing::DirEntry *QDirListing::const_iterator::operator->() const
++
++ Returns a \c{const QDirListing::DirEntry *} to the directory entry this
++ iterator points to.
++ */
++
++ /*!
++ \fn QDirListing::const_iterator::operator++()
++
++ Pre-increment operator.
++ Advances the iterator and returns a reference to it.
++ */
++
++ /*!
++ \fn void QDirListing::const_iterator::operator++(int)
++
++ Post-increment operator.
++
++ \include qdirlisting.cpp std-input-iterator-tag
++
++ //! [post-increment-partially-formed]
++ The return value of post-increment on objects that model
++ \c std::input_iterator is partially-formed (a copy of an iterator that
++ has since been advanced), the only valid operations on such an object
++ are destruction and assignment of a new iterator. Therefore the
++ post-increment operator advances the iterator and returns \c void.
++ //! [post-increment-partially-formed]
++ */
++
++ /*!
++ \internal
++
++ Implements the actual advancing. Not a member function to avoid forcing
++ DirEntry objects (and therefore const_iterator ones) onto the stack.
++ */
++ auto QDirListing::next(DirEntry dirEntry) -> DirEntry
++ {
++ dirEntry.dirListPtr->advance();
++ if (!dirEntry.dirListPtr->hasIterators())
++ return {}; // All done, make `this` equal to the end() iterator
++ return dirEntry;
++ }
++
++ /*!
++ \class QDirListing::DirEntry
++ \inmodule QtCore
++ \ingroup io
++
++ Dereferencing a valid QDirListing::const_iterator returns a DirEntry
++ object.
++
++ 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
++ */
++
++ /*!
++ \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;
++
++ See the QFileInfo methods with the same names.
++ */
++
++ QFileInfo QDirListing::DirEntry::fileInfo() const
++ {
++ return dirListPtr->currentEntryInfo.fileInfo();
++ }
++
++ QString QDirListing::DirEntry::fileName() const
++ {
++ return dirListPtr->currentEntryInfo.fileName();
++ }
++
++ QString QDirListing::DirEntry::baseName() const
++ {
++ return dirListPtr->currentEntryInfo.baseName();
++ }
++
++ QString QDirListing::DirEntry::completeBaseName() const
++ {
++ return dirListPtr->currentEntryInfo.completeBaseName();
++ }
++
++ QString QDirListing::DirEntry::suffix() const
++ {
++ 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