1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2024 Ahmad Samir <a.samirh78@gmail.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
6/*!
7 \since 6.8
8 \class QDirListing
9 \inmodule QtCore
10 \ingroup io
11 \brief The QDirListing class provides an STL-style iterator for directory entries.
12
13 You can use QDirListing to navigate entries of a directory one at a time.
14 It is similar to QDir::entryList() and QDir::entryInfoList(), but because
15 it lists entries one at a time instead of all at once, it scales better
16 and is more suitable for large directories. It also supports listing
17 directory contents recursively, and following symbolic links. Unlike
18 QDir::entryList(), QDirListing does not support sorting.
19
20 The QDirListing constructor takes a directory path string as
21 argument. Here's how to iterate over all entries recursively:
22
23 \snippet code/src_corelib_io_qdirlisting.cpp 0
24
25 Here's how to find and read all regular files filtered by name, recursively:
26
27 \snippet code/src_corelib_io_qdirlisting.cpp 1
28
29 Here's how to list only regular files, recursively:
30 \snippet code/src_corelib_io_qdirlisting.cpp 5
31
32 Here's how to list only regular files and symbolic links to regular
33 files, recursively:
34 \snippet code/src_corelib_io_qdirlisting.cpp 6
35
36//! [std-input-iterator-tag]
37 QDirListing::const_iterator models C++20
38 \l{https://en.cppreference.com/w/cpp/iterator/input_iterator}{std::input_iterator},
39 that is, it is a move-only, forward-only, single-pass iterator, that
40 doesn't allow random access.
41//! [std-input-iterator-tag]
42 It can be used in ranged-for loops (or with C++20 range algorithms that don't
43 require random access iterators). Dereferencing a valid iterator returns
44 a QDirListing::DirEntry object. The (c)end() sentinel marks the end of
45 the iteration. Dereferencing an iterator that is equal to \l{sentinel} is
46 undefined behavior.
47
48 QDirListing::DirEntry offers a subset of QFileInfo's API (for example,
49 fileName(), filePath(), exists()). Internally, DirEntry only constructs
50 a QFileInfo object if needed, that is, if the info hasn't been already
51 fetched by other system functions. You can use DirEntry::fileInfo()
52 to get a QFileInfo. For example:
53
54 \snippet code/src_corelib_io_qdirlisting.cpp 3
55 \snippet code/src_corelib_io_qdirlisting.cpp 4
56
57 \sa QDir, QDir::entryList()
58*/
59
60/*! \enum QDirListing::IteratorFlag
61
62 This enum class describes flags that can be used to configure the behavior
63 of QDirListing. Values from this enumerator can be bitwise OR'ed together.
64
65 \value Default
66 List all entries, that is, files, directories, symbolic links including broken
67 symbolic links (where the target doesn't exist) and special (\e other) system
68 files, see ExcludeOther for details.
69 Hidden files and directories and the special entries \c{.} and \c{..}
70 aren't listed by default.
71
72 \value ExcludeFiles
73 Don't list regular files. When combined with ResolveSymlinks, symbolic
74 links to regular files will be excluded too.
75
76 \value ExcludeDirs
77 Don't list directories. When combined with ResolveSymlinks, symbolic
78 links to directories will be excluded too.
79
80 \omitvalue ExcludeSpecial
81 \value ExcludeOther [since 6.10]
82 Don't list file system entries that are \e not directories, regular files,
83 or symbolic links.
84 \list
85 \li On Unix, a special (other) file system entry is a FIFO, socket,
86 character device, or block device. For more details see the
87 \l{https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknod.html}{\c mknod}
88 manual page.
89 \li On Windows (for historical reasons) \c .lnk files are considered
90 special (other) file system entries.
91 \endlist
92
93 \value ResolveSymlinks
94 Filter symbolic links based on the type of the target of the link,
95 rather than the symbolic link itself. With this flag, broken symbolic
96 links (where the target doesn't exist) are excluded. This flag is
97 ignored on operating systems that don't support symbolic links.
98
99 \value FilesOnly
100 Only regular files will be listed. When combined with ResolveSymlinks,
101 symbolic links to files will also be listed.
102
103 \value DirsOnly
104 Only directories will be listed. When combined with ResolveSymlinks,
105 symbolic links to directories will also be listed.
106
107 \value IncludeHidden
108 List hidden entries. When combined with Recursive, the iteration will
109 recurse into hidden sub-directories as well.
110
111 \value IncludeDotAndDotDot
112 List the \c {.} and \c{..} special entries.
113
114 \value CaseSensitive
115 The file glob patterns in the name filters passed to the QDirListing
116 constructor, will be matched case sensitively (for details, see
117 QDir::setNameFilters()).
118
119 \value Recursive
120 List entries inside all sub-directories as well. When combined with
121 FollowDirSymlinks, symbolic links to directories will be iterated too.
122
123 \value FollowDirSymlinks
124 When combined with Recursive, symbolic links to directories will be
125 iterated too. Symbolic link loops (e.g., link => . or link => ..) are
126 automatically detected and ignored.
127*/
128
129#include "qdirlisting.h"
130#include "qdirentryinfo_p.h"
131
132#include "qdir_p.h"
133#include "qdiriterator.h"
134#include "qabstractfileengine_p.h"
135
136#if QT_CONFIG(regularexpression)
137#include <QtCore/qregularexpression.h>
138#endif
139
140#include <QtCore/private/qfilesystemiterator_p.h>
141#include <QtCore/private/qfilesystementry_p.h>
142#include <QtCore/private/qfilesystemmetadata_p.h>
143#include <QtCore/private/qfilesystemengine_p.h>
144#include <QtCore/private/qfileinfo_p.h>
145#include <QtCore/private/qduplicatetracker_p.h>
146
147#include <memory>
148#include <vector>
149
150QT_BEGIN_NAMESPACE
151
152using namespace Qt::StringLiterals;
153
154static QDirListing::IteratorFlags toDirListingFlags(QDirIterator::IteratorFlags flags)
155{
156 using F = QDirListing::IteratorFlag;
157 QDirListing::IteratorFlags listerFlags;
158
159 if (flags & QDirIterator::NoIteratorFlags)
160 listerFlags.setFlag(flag: F::Default);
161 if (flags & QDirIterator::FollowSymlinks)
162 listerFlags.setFlag(flag: F::FollowDirSymlinks);
163 if (flags & QDirIterator::Subdirectories)
164 listerFlags.setFlag(flag: F::Recursive);
165
166 return listerFlags;
167}
168
169class QDirListingPrivate
170{
171public:
172 void init(bool resolveEngine);
173 void advance();
174 void beginIterating();
175
176 bool entryMatches(QDirEntryInfo &info);
177 void pushDirectory(QDirEntryInfo &info);
178 void pushInitialDirectory();
179
180 void checkAndPushDirectory(QDirEntryInfo &info);
181 bool matchesFilters(QDirEntryInfo &data) const;
182 bool hasIterators() const;
183
184 bool matchesLegacyFilters(QDirEntryInfo &data) const;
185 void setLegacyFilters(QDir::Filters dirFilters, QDirIterator::IteratorFlags dirIteratorFlags)
186 {
187 useLegacyFilters = true;
188 legacyDirFilters = dirFilters;
189 iteratorFlags = toDirListingFlags(flags: dirIteratorFlags);
190 }
191
192 std::unique_ptr<QAbstractFileEngine> engine;
193 QDirEntryInfo initialEntryInfo;
194 QStringList nameFilters;
195 QDirListing::IteratorFlags iteratorFlags;
196 QDirEntryInfo currentEntryInfo;
197
198 bool useLegacyFilters = false;
199 QDir::Filters legacyDirFilters;
200
201#if QT_CONFIG(regularexpression)
202 QList<QRegularExpression> nameRegExps;
203 bool regexMatchesName(const QString &fileName) const
204 {
205 if (nameRegExps.isEmpty())
206 return true;
207 auto hasMatch = [&fileName](const auto &re) { return re.match(fileName).hasMatch(); };
208 return std::any_of(first: nameRegExps.cbegin(), last: nameRegExps.cend(), pred: hasMatch);
209 }
210#endif
211
212 using FEngineIteratorPtr = std::unique_ptr<QAbstractFileEngineIterator>;
213 std::vector<FEngineIteratorPtr> fileEngineIterators;
214#ifndef QT_NO_FILESYSTEMITERATOR
215 using FsIteratorPtr = std::unique_ptr<QFileSystemIterator>;
216 std::vector<FsIteratorPtr> nativeIterators;
217#endif
218
219 // Loop protection
220 QDuplicateTracker<QString> visitedLinks;
221};
222
223void QDirListingPrivate::init(bool resolveEngine = true)
224{
225 if (nameFilters.contains(str: "*"_L1))
226 nameFilters.clear();
227
228 if (useLegacyFilters) {
229 if (legacyDirFilters == QDir::NoFilter)
230 legacyDirFilters = QDir::AllEntries;
231 }
232
233#if QT_CONFIG(regularexpression)
234 nameRegExps.reserve(asize: nameFilters.size());
235
236 const bool isCase = [this] {
237 if (useLegacyFilters)
238 return legacyDirFilters.testAnyFlags(flags: QDir::CaseSensitive);
239 return iteratorFlags.testAnyFlags(flags: QDirListing::IteratorFlag::CaseSensitive);
240 }();
241
242 const auto cs = isCase ? Qt::CaseSensitive : Qt::CaseInsensitive;
243 for (const auto &filter : nameFilters)
244 nameRegExps.emplace_back(args: QRegularExpression::fromWildcard(pattern: filter, cs));
245#endif
246
247 if (resolveEngine) {
248 engine = QFileSystemEngine::createLegacyEngine(entry&: initialEntryInfo.entry,
249 data&: initialEntryInfo.metaData);
250 }
251}
252
253/*!
254 \internal
255
256 Resets the iteration state (if any), so that calling begin()/cbegin()
257 always starts iterating anew.
258*/
259void QDirListingPrivate::beginIterating()
260{
261#ifndef QT_NO_FILESYSTEMITERATOR
262 nativeIterators.clear();
263#endif
264 fileEngineIterators.clear();
265 visitedLinks.clear();
266 pushDirectory(info&: initialEntryInfo);
267}
268
269void QDirListingPrivate::pushDirectory(QDirEntryInfo &entryInfo)
270{
271 const QString path = [&entryInfo] {
272#ifdef Q_OS_WIN
273 if (entryInfo.isSymLink())
274 return entryInfo.canonicalFilePath();
275#endif
276 return entryInfo.filePath();
277 }();
278
279
280 if (iteratorFlags.testAnyFlags(flags: QDirListing::IteratorFlag::FollowDirSymlinks)) {
281 // Stop link loops
282 if (visitedLinks.hasSeen(s: entryInfo.canonicalFilePath()))
283 return;
284 }
285
286 if (engine) {
287 engine->setFileName(path);
288 if (auto it = engine->beginEntryList(path, filters: iteratorFlags, filterNames: nameFilters)) {
289 fileEngineIterators.emplace_back(args: std::move(it));
290 } else {
291 // No iterator; no entry list.
292 }
293 } else {
294#ifndef QT_NO_FILESYSTEMITERATOR
295 QFileSystemEntry *fentry = nullptr;
296 if (entryInfo.fileInfoOpt)
297 fentry = &entryInfo.fileInfoOpt->d_ptr->fileEntry;
298 else
299 fentry = &entryInfo.entry;
300 nativeIterators.emplace_back(args: std::make_unique<QFileSystemIterator>(args&: *fentry, args&: iteratorFlags));
301#else
302 qWarning("Qt was built with -no-feature-filesystemiterator: no files/plugins will be found!");
303#endif
304 }
305}
306
307bool QDirListingPrivate::entryMatches(QDirEntryInfo &entryInfo)
308{
309 checkAndPushDirectory(info&: entryInfo);
310 if (useLegacyFilters)
311 return matchesLegacyFilters(data&: entryInfo);
312 return matchesFilters(data&: entryInfo);
313}
314
315/*!
316 \internal
317
318 Advances the internal iterator, either a QAbstractFileEngineIterator (e.g.
319 QResourceFileEngineIterator) or a QFileSystemIterator (which uses low-level
320 system methods, e.g. readdir() on Unix). The iterators are stored in a
321 vector.
322
323 A typical example of doing recursive iteration:
324 - while iterating directory A we find a sub-dir B
325 - an iterator for B is added to the vector
326 - B's iterator is processed (vector.back()) first; then the loop
327 goes back to processing A's iterator
328*/
329void QDirListingPrivate::advance()
330{
331 // Use get() in both code paths below because the iterator returned by back()
332 // may be invalidated due to reallocation when appending new iterators in
333 // pushDirectory().
334
335 if (engine) {
336 while (!fileEngineIterators.empty()) {
337 // Find the next valid iterator that matches the filters.
338 QAbstractFileEngineIterator *it;
339 while (it = fileEngineIterators.back().get(), it->advance()) {
340 QDirEntryInfo entryInfo;
341 entryInfo.fileInfoOpt = it->currentFileInfo();
342 if (entryMatches(entryInfo)) {
343 currentEntryInfo = std::move(entryInfo);
344 return;
345 }
346 }
347
348 fileEngineIterators.pop_back();
349 }
350 } else {
351#ifndef QT_NO_FILESYSTEMITERATOR
352 QDirEntryInfo entryInfo;
353 while (!nativeIterators.empty()) {
354 // Find the next valid iterator that matches the filters.
355 QFileSystemIterator *it;
356 while (it = nativeIterators.back().get(),
357 it->advance(fileEntry&: entryInfo.entry, metaData&: entryInfo.metaData)) {
358 if (entryMatches(entryInfo)) {
359 currentEntryInfo = std::move(entryInfo);
360 return;
361 }
362 entryInfo = {};
363 }
364
365 nativeIterators.pop_back();
366 }
367#endif
368 }
369}
370
371static bool isDotOrDotDot(QStringView fileName)
372{
373 return fileName == "."_L1 || fileName == ".."_L1;
374}
375
376void QDirListingPrivate::checkAndPushDirectory(QDirEntryInfo &entryInfo)
377{
378 using F = QDirListing::IteratorFlag;
379 // If we're doing flat iteration, we're done.
380 if (!iteratorFlags.testAnyFlags(flags: F::Recursive))
381 return;
382
383 // Follow symlinks only when asked
384 if (!iteratorFlags.testAnyFlags(flags: F::FollowDirSymlinks) && entryInfo.isSymLink())
385 return;
386
387 // Never follow . and ..
388 if (isDotOrDotDot(fileName: entryInfo.fileName()))
389 return;
390
391 // No hidden directories unless requested
392 const bool includeHidden = [this]() {
393 if (useLegacyFilters)
394 return legacyDirFilters.testAnyFlags(flags: QDir::AllDirs | QDir::Hidden);
395 return iteratorFlags.testAnyFlags(flags: QDirListing::IteratorFlag::IncludeHidden);
396 }();
397 if (!includeHidden && entryInfo.isHidden())
398 return;
399
400 // Never follow non-directory entries
401 if (!entryInfo.isDir())
402 return;
403
404 pushDirectory(entryInfo);
405}
406
407/*!
408 \internal
409
410 Works the same as matchesFilters() but for the old QDir::Filters.
411*/
412bool QDirListingPrivate::matchesLegacyFilters(QDirEntryInfo &entryInfo) const
413{
414 Q_ASSERT(useLegacyFilters);
415
416 const QString &fileName = entryInfo.fileName();
417 if (fileName.isEmpty())
418 return false;
419
420 auto &filters = legacyDirFilters;
421
422 // filter . and ..?
423 const bool dotOrDotDot = isDotOrDotDot(fileName);
424 const qsizetype fileNameSize = fileName.size();
425 if ((filters & QDir::NoDot) && dotOrDotDot && fileNameSize == 1)
426 return false;
427 if ((filters & QDir::NoDotDot) && dotOrDotDot && fileNameSize == 2)
428 return false;
429
430 // name filter
431#if QT_CONFIG(regularexpression)
432 // Pass all entries through name filters, except dirs if AllDirs is set
433 if (!(filters.testAnyFlags(flags: QDir::AllDirs) && entryInfo.isDir())) {
434 if (!regexMatchesName(fileName))
435 return false;
436 }
437#endif
438 // skip symlinks
439 const bool skipSymlinks = filters.testAnyFlag(flag: QDir::NoSymLinks);
440 const bool includeSystem = filters.testAnyFlag(flag: QDir::System);
441 if (skipSymlinks && entryInfo.isSymLink()) {
442 // The only reason to save this file is if it is a broken link and we are requesting system files.
443 if (!includeSystem || entryInfo.exists())
444 return false;
445 }
446
447 // filter hidden
448 const bool includeHidden = filters.testAnyFlag(flag: QDir::Hidden);
449 if (!includeHidden && !dotOrDotDot && entryInfo.isHidden())
450 return false;
451
452 // filter system files
453 if (!includeSystem) {
454 if (!entryInfo.isFile() && !entryInfo.isDir() && !entryInfo.isSymLink())
455 return false;
456 if (entryInfo.isSymLink() && !entryInfo.exists())
457 return false;
458 }
459
460 // skip directories
461 const bool skipDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
462 if (skipDirs && entryInfo.isDir())
463 return false;
464
465 // skip files
466 const bool skipFiles = !(filters & QDir::Files);
467 if (skipFiles && entryInfo.isFile())
468 // Basically we need a reason not to exclude this file otherwise we just eliminate it.
469 return false;
470
471 // filter permissions
472 const auto perms = filters & QDir::PermissionMask;
473 const bool filterPermissions = perms != 0 && perms != QDir::PermissionMask;
474 if (filterPermissions) {
475 const bool doWritable = filters.testAnyFlags(flags: QDir::Writable);
476 const bool doExecutable = filters.testAnyFlags(flags: QDir::Executable);
477 const bool doReadable = filters.testAnyFlags(flags: QDir::Readable);
478 if ((doReadable && !entryInfo.isReadable())
479 || (doWritable && !entryInfo.isWritable())
480 || (doExecutable && !entryInfo.isExecutable())) {
481 return false;
482 }
483 }
484
485 return true;
486}
487
488/*!
489 \internal
490
491 This function returns \c true if the current entry matches the filters
492 (i.e., the current entry will be returned as part of the directory
493 iteration); otherwise, \c false is returned.
494*/
495bool QDirListingPrivate::matchesFilters(QDirEntryInfo &entryInfo) const
496{
497 using F = QDirListing::IteratorFlag;
498
499 const QString &fileName = entryInfo.fileName();
500 if (fileName.isEmpty())
501 return false;
502
503 // name filter
504#if QT_CONFIG(regularexpression)
505 if (!regexMatchesName(fileName))
506 return false;
507#endif // QT_CONFIG(regularexpression)
508
509 if (isDotOrDotDot(fileName))
510 return iteratorFlags.testFlags(flags: F::IncludeDotAndDotDot);
511
512 if (!iteratorFlags.testAnyFlag(flag: F::IncludeHidden) && entryInfo.isHidden())
513 return false;
514
515 // With ResolveSymlinks, we look at the type of the link's target,
516 // and exclude broken symlinks (where the target doesn't exist).
517 if (iteratorFlags.testAnyFlag(flag: F::ResolveSymlinks)) {
518 if (entryInfo.isSymLink() && !entryInfo.exists())
519 return false;
520 } else if ((iteratorFlags.testAnyFlags(flags: F::FilesOnly)
521 || iteratorFlags.testAnyFlags(flags: F::DirsOnly)) && entryInfo.isSymLink()) {
522 return false; // symlink is not a file or dir
523 }
524
525 if (iteratorFlags.testAnyFlag(flag: F::ExcludeOther)
526 && !entryInfo.isFile() && !entryInfo.isDir() && !entryInfo.isSymLink()) {
527 return false;
528 }
529
530 if (iteratorFlags.testAnyFlags(flags: F::ExcludeDirs) && entryInfo.isDir())
531 return false;
532
533 if (iteratorFlags.testAnyFlags(flags: F::ExcludeFiles) && entryInfo.isFile())
534 return false;
535
536 return true;
537}
538
539bool QDirListingPrivate::hasIterators() const
540{
541 if (engine)
542 return !fileEngineIterators.empty();
543
544#if !defined(QT_NO_FILESYSTEMITERATOR)
545 return !nativeIterators.empty();
546#endif
547
548 return false;
549}
550
551/*!
552 Constructs a QDirListing that can iterate over \a path.
553
554 You can pass options via \a flags to control how the directory should
555 be iterated.
556
557 By default, \a flags is IteratorFlag::Default.
558
559 \sa IteratorFlags
560*/
561QDirListing::QDirListing(const QString &path, IteratorFlags flags)
562 : d(new QDirListingPrivate)
563{
564 d->initialEntryInfo.entry = QFileSystemEntry(path);
565 d->iteratorFlags = flags;
566 d->init();
567}
568
569/*!
570 Constructs a QDirListing that can iterate over \a path.
571
572 You can pass options via \a flags to control how the directory should
573 be iterated. By default, \a flags is IteratorFlag::Default.
574
575 The listed entries will be filtered according to the file glob patterns
576 in \a nameFilters, which are converted to a regular expression using
577 QRegularExpression::fromWildcard (see QDir::setNameFilters() for more
578 details).
579
580 For example, the following iterator could be used to iterate over audio
581 files:
582
583 \snippet code/src_corelib_io_qdirlisting.cpp 2
584
585 Sometimes you can filter by name more efficiently by iterating over the
586 entries with a range-for loop, using string comparison. For example:
587
588 \snippet code/src_corelib_io_qdirlisting.cpp 7
589
590 \sa IteratorFlags, QDir::setNameFilters()
591*/
592QDirListing::QDirListing(const QString &path, const QStringList &nameFilters, IteratorFlags flags)
593 : d(new QDirListingPrivate)
594{
595 d->initialEntryInfo.entry = QFileSystemEntry(path);
596 d->nameFilters = nameFilters;
597 d->iteratorFlags = flags;
598 d->init();
599}
600
601/*!
602 \internal
603
604 Only used by classes that still have to use QDir::Filters; for example,
605 QDir, such usage may be deprecated at some point.
606
607 \a qdirFilters is converted to QDir::Filters and \a qdirIteratorFlags is
608 converted to QDirIterator::IteratorFlags (qdirlisting.h can't include
609 qdir.h or qdiriterator.h) and used to control the filtering of the
610 dir entries.
611*/
612QDirListing::QDirListing(const QString &path, const QStringList &nameFilters, uint qdirFilters,
613 uint qdirIteratorFlags)
614 : d(new QDirListingPrivate)
615{
616 d->initialEntryInfo.entry = QFileSystemEntry(path);
617 d->nameFilters = nameFilters;
618 d->setLegacyFilters(dirFilters: QDir::Filters::fromInt(i: qdirFilters),
619 dirIteratorFlags: QDirIterator::IteratorFlags::fromInt(i: qdirIteratorFlags));
620 d->init();
621}
622
623/*!
624 \fn QDirListing::QDirListing(QDirListing &&other)
625
626 Move constructor. Moves \a other into this QDirListing.
627
628//! [partially-formed]
629 \note The moved-from object \a other is placed in a partially-formed state,
630 in which the only valid operations are destruction and assignment of a new
631 value.
632//! [partially-formed]
633*/
634
635/*!
636 \fn QDirListing &QDirListing::operator=(QDirListing &&other)
637
638 Move-assigns \a other to this QDirListing.
639
640 \include qdirlisting.cpp partially-formed
641*/
642
643/*!
644 Destroys the QDirListing.
645*/
646QDirListing::~QDirListing()
647{
648 delete d;
649}
650
651/*!
652 Returns the directory path used to construct this QDirListing.
653*/
654QString QDirListing::iteratorPath() const
655{
656 return d->initialEntryInfo.filePath();
657}
658
659/*!
660 Returns the set of IteratorFlags used to construct this QDirListing.
661*/
662QDirListing::IteratorFlags QDirListing::iteratorFlags() const
663{
664 return d->iteratorFlags;
665}
666
667/*!
668 Returns the list of file name glob filters used to construct this
669 QDirListing.
670*/
671QStringList QDirListing::nameFilters() const
672{
673 return d->nameFilters;
674}
675
676/*!
677 \class QDirListing::const_iterator
678 \since 6.8
679 \inmodule QtCore
680 \ingroup io
681
682 The iterator type returned by QDirListing::cbegin().
683
684//! [dirlisting-iterator-behavior]
685 \list
686 \li This is a forward-only, single-pass iterator (you cannot iterate
687 directory entries in reverse order)
688 \li Can't be copied, only \c{std::move()}d.
689 \li \include qdirlisting.cpp post-increment-partially-formed
690 \li Doesn't allow random access
691 \li Can be used in ranged-for loops; or with C++20 std::ranges algorithms
692 that don't require random access iterators
693 \li Dereferencing a valid iterator returns a \c{const DirEntry &}
694 \li (c)end() returns a \l QDirListing::sentinel that signals the end of
695 the iteration. Dereferencing an iterator that compares equal to end()
696 is undefined behavior
697 \endlist
698//! [dirlisting-iterator-behavior]
699
700 \include qdirlisting.cpp ranges-algorithms-note
701
702 \sa QDirListing, QDirListing::sentinel, QDirListing::DirEntry
703*/
704
705/*!
706 \typealias QDirListing::const_iterator::reference
707
708 A typedef for \c {const QDirListing::DirEntry &}.
709*/
710
711/*!
712 \typealias QDirListing::const_iterator::pointer
713
714 A typedef for \c {const QDirListing::DirEntry *}.
715*/
716
717/*!
718 \class QDirListing::sentinel
719 \since 6.8
720 \inmodule QtCore
721 \ingroup io
722
723 \l QDirListing returns an object of this type to signal the end of
724 iteration. Dereferencing a \l QDirListing::const_iterator that is
725 equal to \c sentinel{} is undefined behavior.
726
727 \include qdirlisting.cpp ranges-algorithms-note
728
729 \sa QDirListing, QDirListing::const_iterator, QDirListing::DirEntry
730*/
731
732/*!
733 \fn QDirListing::const_iterator QDirListing::begin() const
734 \fn QDirListing::const_iterator QDirListing::cbegin() const
735 \fn QDirListing::sentinel QDirListing::end() const
736 \fn QDirListing::sentinel QDirListing::cend() const
737
738 (c)begin() returns a QDirListing::const_iterator that can be used to
739 iterate over directory entries.
740
741 \include qdirlisting.cpp dirlisting-iterator-behavior
742
743 \note Each time (c)begin() is called on the same QDirListing object,
744 the internal state is reset and the iteration starts anew.
745
746 (Some of the above restrictions are dictated by the underlying system
747 library functions' implementation).
748
749 For example:
750 \snippet code/src_corelib_io_qdirlisting.cpp 0
751
752 Here's how to find and read all files filtered by name, recursively:
753 \snippet code/src_corelib_io_qdirlisting.cpp 1
754
755//! [ranges-algorithms-note]
756 \note The "classical" STL algorithms don't support iterator/sentinel, so
757 you need to use C++20 std::ranges algorithms for QDirListing, or else a
758 3rd-party library that provides range-based algorithms in C++17.
759//! [ranges-algorithms-note]
760
761 \sa QDirListing::DirEntry
762*/
763QDirListing::const_iterator QDirListing::begin() const
764{
765 d->beginIterating();
766 const_iterator it{d};
767 ++it;
768 return it;
769}
770
771/*!
772 \fn const QDirListing::DirEntry &QDirListing::const_iterator::operator*() const
773
774 Returns a \c{const QDirListing::DirEntry &} of the directory entry this
775 iterator points to.
776*/
777
778/*!
779 \fn const QDirListing::DirEntry *QDirListing::const_iterator::operator->() const
780
781 Returns a \c{const QDirListing::DirEntry *} to the directory entry this
782 iterator points to.
783*/
784
785/*!
786 \fn QDirListing::const_iterator::operator++()
787
788 Pre-increment operator.
789 Advances the iterator and returns a reference to it.
790*/
791
792/*!
793 \fn void QDirListing::const_iterator::operator++(int)
794
795 Post-increment operator.
796
797 \include qdirlisting.cpp std-input-iterator-tag
798
799//! [post-increment-partially-formed]
800 The return value of post-increment on objects that model
801 \c std::input_iterator is partially-formed (a copy of an iterator that
802 has since been advanced), the only valid operations on such an object
803 are destruction and assignment of a new iterator. Therefore the
804 post-increment operator advances the iterator and returns \c void.
805//! [post-increment-partially-formed]
806*/
807
808/*!
809 \internal
810
811 Implements the actual advancing. Not a member function to avoid forcing
812 DirEntry objects (and therefore const_iterator ones) onto the stack.
813*/
814auto QDirListing::next(DirEntry dirEntry) -> DirEntry
815{
816 dirEntry.dirListPtr->advance();
817 if (!dirEntry.dirListPtr->hasIterators())
818 return {}; // All done, make `this` equal to the end() iterator
819 return dirEntry;
820}
821
822/*!
823 \class QDirListing::DirEntry
824 \inmodule QtCore
825 \ingroup io
826
827 Dereferencing a valid QDirListing::const_iterator returns a DirEntry
828 object.
829
830 DirEntry offers a subset of QFileInfo's API (for example, fileName(),
831 filePath(), exists()). Internally, DirEntry only constructs a QFileInfo
832 object if needed, that is, if the info hasn't been already fetched
833 by other system functions. You can use DirEntry::fileInfo() to get a
834 QFileInfo. For example:
835
836 \snippet code/src_corelib_io_qdirlisting.cpp 3
837
838 \snippet code/src_corelib_io_qdirlisting.cpp 4
839*/
840
841/*!
842 \fn QFileInfo QDirListing::DirEntry::fileInfo() const
843 \fn QString QDirListing::DirEntry::fileName() const
844 \fn QString QDirListing::DirEntry::baseName() const
845 \fn QString QDirListing::DirEntry::completeBaseName() const
846 \fn QString QDirListing::DirEntry::suffix() const
847 \fn QString QDirListing::DirEntry::bundleName() const
848 \fn QString QDirListing::DirEntry::completeSuffix() const
849 \fn QString QDirListing::DirEntry::filePath() const
850 \fn QString QDirListing::DirEntry::canonicalFilePath() const
851 \fn QString QDirListing::DirEntry::absoluteFilePath() const
852 \fn QString QDirListing::DirEntry::absolutePath() const
853 \fn bool QDirListing::DirEntry::isDir() const
854 \fn bool QDirListing::DirEntry::isFile() const
855 \fn bool QDirListing::DirEntry::isSymLink() const
856 \fn bool QDirListing::DirEntry::exists() const
857 \fn bool QDirListing::DirEntry::isHidden() const
858 \fn bool QDirListing::DirEntry::isReadable() const
859 \fn bool QDirListing::DirEntry::isWritable() const
860 \fn bool QDirListing::DirEntry::isExecutable() const
861 \fn qint64 QDirListing::DirEntry::size() const
862 \fn QDateTime QDirListing::DirEntry::fileTime(QFile::FileTime type, const QTimeZone &tz) const
863 \fn QDateTime QDirListing::DirEntry::birthTime(const QTimeZone &tz) const;
864 \fn QDateTime QDirListing::DirEntry::metadataChangeTime(const QTimeZone &tz) const;
865 \fn QDateTime QDirListing::DirEntry::lastModified(const QTimeZone &tz) const;
866 \fn QDateTime QDirListing::DirEntry::lastRead(const QTimeZone &tz) const;
867
868 See the QFileInfo methods with the same names.
869*/
870
871QFileInfo QDirListing::DirEntry::fileInfo() const
872{
873 return dirListPtr->currentEntryInfo.fileInfo();
874}
875
876QString QDirListing::DirEntry::fileName() const
877{
878 return dirListPtr->currentEntryInfo.fileName();
879}
880
881QString QDirListing::DirEntry::baseName() const
882{
883 return dirListPtr->currentEntryInfo.baseName();
884}
885
886QString QDirListing::DirEntry::completeBaseName() const
887{
888 return dirListPtr->currentEntryInfo.completeBaseName();
889}
890
891QString QDirListing::DirEntry::suffix() const
892{
893 return dirListPtr->currentEntryInfo.suffix();
894}
895
896QString QDirListing::DirEntry::bundleName() const
897{
898 return dirListPtr->currentEntryInfo.bundleName();
899}
900
901QString QDirListing::DirEntry::completeSuffix() const
902{
903 return dirListPtr->currentEntryInfo.completeSuffix();
904}
905
906QString QDirListing::DirEntry::filePath() const
907{
908 return dirListPtr->currentEntryInfo.filePath();
909}
910
911QString QDirListing::DirEntry::canonicalFilePath() const
912{
913 return dirListPtr->currentEntryInfo.canonicalFilePath();
914}
915
916QString QDirListing::DirEntry::absoluteFilePath() const
917{
918 return dirListPtr->currentEntryInfo.absoluteFilePath();
919}
920
921QString QDirListing::DirEntry::absolutePath() const
922{
923 return dirListPtr->currentEntryInfo.absolutePath();
924}
925
926bool QDirListing::DirEntry::isDir() const
927{
928 return dirListPtr->currentEntryInfo.isDir();
929}
930
931bool QDirListing::DirEntry::isFile() const
932{
933 return dirListPtr->currentEntryInfo.isFile();
934}
935
936bool QDirListing::DirEntry::isSymLink() const
937{
938 return dirListPtr->currentEntryInfo.isSymLink();
939}
940
941bool QDirListing::DirEntry::exists() const
942{
943 return dirListPtr->currentEntryInfo.exists();
944}
945
946bool QDirListing::DirEntry::isHidden() const
947{
948 return dirListPtr->currentEntryInfo.isHidden();
949}
950
951bool QDirListing::DirEntry::isReadable() const
952{
953 return dirListPtr->currentEntryInfo.isReadable();
954}
955
956bool QDirListing::DirEntry::isWritable() const
957{
958 return dirListPtr->currentEntryInfo.isWritable();
959}
960
961bool QDirListing::DirEntry::isExecutable() const
962{
963 return dirListPtr->currentEntryInfo.isExecutable();
964}
965
966qint64 QDirListing::DirEntry::size() const
967{
968 return dirListPtr->currentEntryInfo.size();
969}
970
971QDateTime QDirListing::DirEntry::fileTime(QFile::FileTime type, const QTimeZone &tz) const
972{
973 return dirListPtr->currentEntryInfo.fileTime(type, tz);
974}
975
976QT_END_NAMESPACE
977

source code of qtbase/src/corelib/io/qdirlisting.cpp