Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdirlisting.cpp
Go to the documentation of this file.
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
63#include "qdirlisting.h"
64#include "qdirentryinfo_p.h"
65
66#include "qdir_p.h"
68
69#include <QtCore/qset.h>
70
71#if QT_CONFIG(regularexpression)
72#include <QtCore/qregularexpression.h>
73#endif
74
75#include <QtCore/private/qfilesystemiterator_p.h>
76#include <QtCore/private/qfilesystementry_p.h>
77#include <QtCore/private/qfilesystemmetadata_p.h>
78#include <QtCore/private/qfilesystemengine_p.h>
79#include <QtCore/private/qfileinfo_p.h>
80#include <QtCore/private/qduplicatetracker_p.h>
81
82#include <memory>
83#include <stack>
84#include <vector>
85
87
88using namespace Qt::StringLiterals;
89
91{
92public:
93 void init(bool resolveEngine);
94 void advance();
95
99
101 bool matchesFilters(QDirEntryInfo &data) const;
102 bool hasIterators() const;
103
104 std::unique_ptr<QAbstractFileEngine> engine;
107 QDir::Filters filters;
108 QDirListing::IteratorFlags iteratorFlags;
110
111#if QT_CONFIG(regularexpression)
112 QList<QRegularExpression> nameRegExps;
113#endif
114
115 using FEngineIteratorPtr = std::unique_ptr<QAbstractFileEngineIterator>;
116 std::stack<FEngineIteratorPtr, std::vector<FEngineIteratorPtr>> fileEngineIterators;
117#ifndef QT_NO_FILESYSTEMITERATOR
118 using FsIteratorPtr = std::unique_ptr<QFileSystemIterator>;
119 std::stack<FsIteratorPtr, std::vector<FsIteratorPtr>> nativeIterators;
120#endif
121
122 // Loop protection
123 QDuplicateTracker<QString> visitedLinks;
124};
125
126void QDirListingPrivate::init(bool resolveEngine = true)
127{
128 if (nameFilters.contains("*"_L1))
129 nameFilters.clear();
130
131 if (filters == QDir::NoFilter)
133
134#if QT_CONFIG(regularexpression)
135 nameRegExps.reserve(nameFilters.size());
136 const auto cs = filters.testAnyFlags(QDir::CaseSensitive) ? Qt::CaseSensitive
138 for (const auto &filter : nameFilters)
139 nameRegExps.emplace_back(QRegularExpression::fromWildcard(filter, cs));
140#endif
141
142 if (resolveEngine) {
144 initialEntryInfo.metaData);
145 }
146
148}
149
151{
152 const QString path = [&entryInfo] {
153#ifdef Q_OS_WIN
154 if (entryInfo.isSymLink())
155 return entryInfo.canonicalFilePath();
156#endif
157 return entryInfo.filePath();
158 }();
159
160
162 // Stop link loops
163 if (visitedLinks.hasSeen(entryInfo.canonicalFilePath()))
164 return;
165 }
166
167 if (engine) {
168 engine->setFileName(path);
169 if (auto it = engine->beginEntryList(path, filters, nameFilters)) {
170 fileEngineIterators.emplace(std::move(it));
171 } else {
172 // No iterator; no entry list.
173 }
174 } else {
175#ifndef QT_NO_FILESYSTEMITERATOR
176 QFileSystemEntry *fentry = nullptr;
177 if (entryInfo.fileInfoOpt)
178 fentry = &entryInfo.fileInfoOpt->d_ptr->fileEntry;
179 else
180 fentry = &entryInfo.entry;
181 nativeIterators.emplace(std::make_unique<QFileSystemIterator>(*fentry, filters));
182#else
183 qWarning("Qt was built with -no-feature-filesystemiterator: no files/plugins will be found!");
184#endif
185 }
186}
187
189{
190 checkAndPushDirectory(entryInfo);
191 return matchesFilters(entryInfo);
192}
193
210{
211 // Use get() in both code paths below because the iterator returned by top()
212 // may be invalidated due to reallocation when appending new iterators in
213 // pushDirectory().
214
215 if (engine) {
216 while (!fileEngineIterators.empty()) {
217 // Find the next valid iterator that matches the filters.
219 while (it = fileEngineIterators.top().get(), it->advance()) {
220 QDirEntryInfo entryInfo;
221 entryInfo.fileInfoOpt = it->currentFileInfo();
222 if (entryMatches(entryInfo)) {
223 currentEntryInfo = std::move(entryInfo);
224 return;
225 }
226 }
227
229 }
230 } else {
231#ifndef QT_NO_FILESYSTEMITERATOR
232 QDirEntryInfo entryInfo;
233 while (!nativeIterators.empty()) {
234 // Find the next valid iterator that matches the filters.
236 while (it = nativeIterators.top().get(), it->advance(entryInfo.entry, entryInfo.metaData)) {
237 if (entryMatches(entryInfo)) {
238 currentEntryInfo = std::move(entryInfo);
239 return;
240 }
241 entryInfo = {};
242 }
243
244 nativeIterators.pop();
245 }
246#endif
247 }
248}
249
251{
253 // If we're doing flat iteration, we're done.
254 if (!iteratorFlags.testAnyFlags(F::Recursive))
255 return;
256
257 // Never follow non-directory entries
258 if (!entryInfo.isDir())
259 return;
260
261 // Follow symlinks only when asked
262 if (!iteratorFlags.testAnyFlags(F::FollowSymlinks) && entryInfo.isSymLink())
263 return;
264
265 // Never follow . and ..
266 const QString &fileName = entryInfo.fileName();
267 if ("."_L1 == fileName || ".."_L1 == fileName)
268 return;
269
270 // No hidden directories unless requested
271 if (!filters.testAnyFlags(QDir::AllDirs | QDir::Hidden) && entryInfo.isHidden())
272 return;
273
274 pushDirectory(entryInfo);
275}
276
285{
286 const QString &fileName = entryInfo.fileName();
287 if (fileName.isEmpty())
288 return false;
289
290 // filter . and ..?
291 const qsizetype fileNameSize = fileName.size();
292 const bool dotOrDotDot = fileName[0] == u'.'
293 && ((fileNameSize == 1)
294 ||(fileNameSize == 2 && fileName[1] == u'.'));
295 if ((filters & QDir::NoDot) && dotOrDotDot && fileNameSize == 1)
296 return false;
297 if ((filters & QDir::NoDotDot) && dotOrDotDot && fileNameSize == 2)
298 return false;
299
300 // name filter
301#if QT_CONFIG(regularexpression)
302 // Pass all entries through name filters, except dirs if AllDirs is set
303 if (!nameRegExps.isEmpty() && !(filters.testAnyFlags(QDir::AllDirs) && entryInfo.isDir())) {
304 auto regexMatchesName = [&fileName](const auto &re) {
305 return re.match(fileName).hasMatch();
306 };
307 if (std::none_of(nameRegExps.cbegin(), nameRegExps.cend(), regexMatchesName))
308 return false;
309 }
310#endif
311 // skip symlinks
312 const bool skipSymlinks = filters.testAnyFlag(QDir::NoSymLinks);
313 const bool includeSystem = filters.testAnyFlag(QDir::System);
314 if (skipSymlinks && entryInfo.isSymLink()) {
315 // The only reason to save this file is if it is a broken link and we are requesting system files.
316 if (!includeSystem || entryInfo.exists())
317 return false;
318 }
319
320 // filter hidden
321 const bool includeHidden = filters.testAnyFlag(QDir::Hidden);
322 if (!includeHidden && !dotOrDotDot && entryInfo.isHidden())
323 return false;
324
325 // filter system files
326 if (!includeSystem) {
327 if (!entryInfo.isFile() && !entryInfo.isDir() && !entryInfo.isSymLink())
328 return false;
329 if (entryInfo.isSymLink() && !entryInfo.exists())
330 return false;
331 }
332
333 // skip directories
334 const bool skipDirs = !(filters & (QDir::Dirs | QDir::AllDirs));
335 if (skipDirs && entryInfo.isDir())
336 return false;
337
338 // skip files
339 const bool skipFiles = !(filters & QDir::Files);
340 if (skipFiles && entryInfo.isFile())
341 // Basically we need a reason not to exclude this file otherwise we just eliminate it.
342 return false;
343
344 // filter permissions
345 const auto perms = filters & QDir::PermissionMask;
346 const bool filterPermissions = perms != 0 && perms != QDir::PermissionMask;
347 if (filterPermissions) {
348 const bool doWritable = filters.testAnyFlags(QDir::Writable);
349 const bool doExecutable = filters.testAnyFlags(QDir::Executable);
350 const bool doReadable = filters.testAnyFlags(QDir::Readable);
351 if ((doReadable && !entryInfo.isReadable())
352 || (doWritable && !entryInfo.isWritable())
353 || (doExecutable && !entryInfo.isExecutable())) {
354 return false;
355 }
356 }
357
358 return true;
359}
360
362{
363 if (engine)
364 return !fileEngineIterators.empty();
365
366#if !defined(QT_NO_FILESYSTEMITERATOR)
367 return !nativeIterators.empty();
368#endif
369
370 return false;
371}
372
389 : d(new QDirListingPrivate)
390{
391 const QDirPrivate *other = dir.d_ptr.constData();
392 d->initialEntryInfo.entry = other->dirEntry;
393 d->nameFilters = other->nameFilters;
394 d->filters = other->filters;
395 d->iteratorFlags = flags;
396 const bool resolveEngine = other->fileEngine ? true : false;
397 d->init(resolveEngine);
398}
399
413QDirListing::QDirListing(const QString &path, QDir::Filters filters, IteratorFlags flags)
414 : d(new QDirListingPrivate)
415{
416 d->initialEntryInfo.entry = QFileSystemEntry(path);
417 d->filters = filters;
418 d->iteratorFlags = flags;
419 d->init();
420}
421
432 : d(new QDirListingPrivate)
433{
434 d->initialEntryInfo.entry = QFileSystemEntry(path);
435 d->filters = QDir::NoFilter;
436 d->iteratorFlags = flags;
437 d->init();
438}
439
458QDirListing::QDirListing(const QString &path, const QStringList &nameFilters, QDir::Filters filters,
459 IteratorFlags flags)
460 : d(new QDirListingPrivate)
461{
462 d->initialEntryInfo.entry = QFileSystemEntry(path);
463 d->nameFilters = nameFilters;
464 d->filters = filters;
465 d->iteratorFlags = flags;
466 d->init();
467}
468
472QDirListing::~QDirListing() = default;
473
478{
479 return d->initialEntryInfo.filePath();
480}
481
508{
509 const_iterator it{d.get()};
510 return ++it;
511}
512
531{
532 dirListPtr->advance();
533 if (!dirListPtr->hasIterators())
534 *this = {}; // All done, make `this` the end() iterator
535 return *this;
536}
537
569{
570 return dirListPtr->currentEntryInfo.fileInfo();
571}
572
574{
575 return dirListPtr->currentEntryInfo.fileName();
576}
577
579{
580 return dirListPtr->currentEntryInfo.baseName();
581}
582
584{
585 return dirListPtr->currentEntryInfo.completeBaseName();
586}
587
589{
590 return dirListPtr->currentEntryInfo.suffix();
591}
592
594{
595 return dirListPtr->currentEntryInfo.bundleName();
596}
597
599{
600 return dirListPtr->currentEntryInfo.completeSuffix();
601}
602
604{
605 return dirListPtr->currentEntryInfo.filePath();
606}
607
609{
610 return dirListPtr->currentEntryInfo.canonicalFilePath();
611}
612
614{
615 return dirListPtr->currentEntryInfo.absoluteFilePath();
616}
617
619{
620 return dirListPtr->currentEntryInfo.absolutePath();
621}
622
624{
625 return dirListPtr->currentEntryInfo.isDir();
626}
627
629{
630 return dirListPtr->currentEntryInfo.isFile();
631}
632
634{
635 return dirListPtr->currentEntryInfo.isSymLink();
636}
637
639{
640 return dirListPtr->currentEntryInfo.exists();
641}
642
644{
645 return dirListPtr->currentEntryInfo.isHidden();
646}
647
649{
650 return dirListPtr->currentEntryInfo.isReadable();
651}
652
654{
655 return dirListPtr->currentEntryInfo.isWritable();
656}
657
659{
660 return dirListPtr->currentEntryInfo.isExecutable();
661}
662
664{
665 return dirListPtr->currentEntryInfo.size();
666}
667
669{
670 return dirListPtr->currentEntryInfo.fileTime(type, tz);
671}
672
The QAbstractFileEngineIterator class provides an iterator interface for custom file engines.
\inmodule QtCore\reentrant
Definition qdatetime.h:283
bool entryMatches(QDirEntryInfo &info)
QDirListing::IteratorFlags iteratorFlags
std::unique_ptr< QAbstractFileEngineIterator > FEngineIteratorPtr
void pushInitialDirectory()
QStringList nameFilters
void checkAndPushDirectory(QDirEntryInfo &info)
std::unique_ptr< QFileSystemIterator > FsIteratorPtr
std::unique_ptr< QAbstractFileEngine > engine
bool hasIterators() const
std::stack< FsIteratorPtr, std::vector< FsIteratorPtr > > nativeIterators
std::stack< FEngineIteratorPtr, std::vector< FEngineIteratorPtr > > fileEngineIterators
QDir::Filters filters
bool matchesFilters(QDirEntryInfo &data) const
QDirEntryInfo initialEntryInfo
QDuplicateTracker< QString > visitedLinks
QDirEntryInfo currentEntryInfo
void pushDirectory(QDirEntryInfo &info)
void init(bool resolveEngine)
QFileInfo fileInfo() const
QString suffix() const
QString absoluteFilePath() const
QString baseName() const
QString bundleName() const
QString completeBaseName() const
QString completeSuffix() const
bool isExecutable() const
QString filePath() const
QDateTime fileTime(QFile::FileTime type, const QTimeZone &tz) const
QString canonicalFilePath() const
QString fileName() const
QString absolutePath() const
Q_CORE_EXPORT const_iterator & operator++()
Advances the iterator and returns a reference to it.
IteratorFlag
This enum class describes flags can be used to configure the behavior of QDirListing.
Definition qdirlisting.h:20
const_iterator begin() const
QDirListing(const QDir &dir, IteratorFlags flags=IteratorFlag::NoFlag)
Constructs a QDirListing that can iterate over dir's entries, using dir's name filters and the QDir::...
~QDirListing()
Destroys the QDirListing.
QString iteratorPath() const
Returns the directory path used to construct this QDirListing.
\inmodule QtCore
Definition qdir.h:20
@ Executable
Definition qdir.h:31
@ CaseSensitive
Definition qdir.h:41
@ Files
Definition qdir.h:23
@ PermissionMask
Definition qdir.h:32
@ Hidden
Definition qdir.h:35
@ AllEntries
Definition qdir.h:26
@ AllDirs
Definition qdir.h:40
@ NoSymLinks
Definition qdir.h:25
@ NoDotDot
Definition qdir.h:43
@ NoFilter
Definition qdir.h:46
@ Readable
Definition qdir.h:29
@ Writable
Definition qdir.h:30
@ System
Definition qdir.h:36
@ NoDot
Definition qdir.h:42
@ Dirs
Definition qdir.h:22
bool hasSeen(const T &s)
static std::unique_ptr< QAbstractFileEngine > createLegacyEngine(QFileSystemEntry &entry, QFileSystemMetaData &data)
static QRegularExpression fromWildcard(QStringView pattern, Qt::CaseSensitivity cs=Qt::CaseInsensitive, WildcardConversionOptions options=DefaultWildcardConversion)
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
\inmodule QtCore
Definition qtimezone.h:26
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ CaseInsensitive
@ CaseSensitive
#define qWarning
Definition qlogging.h:166
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLbitfield flags
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLbyte GLbyte tz
GLsizei const GLchar *const * path
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
QSharedPointer< T > other(t)
[5]
QString dir
[11]
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
QHostInfo info
[0]