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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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