| 1 | // Copyright (C) 2016 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | // Qt-Security score:significant reason:default |
| 4 | |
| 5 | /*! |
| 6 | \since 4.3 |
| 7 | \class QDirIterator |
| 8 | \inmodule QtCore |
| 9 | \ingroup io |
| 10 | \brief The QDirIterator class provides an iterator for directory entrylists. |
| 11 | |
| 12 | You can use QDirIterator 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(), QDirIterator does not support sorting. |
| 18 | |
| 19 | The QDirIterator constructor takes a QDir or a directory as |
| 20 | argument. After construction, the iterator is located before the first |
| 21 | directory entry. Here's how to iterate over all the entries sequentially: |
| 22 | |
| 23 | \snippet code/src_corelib_io_qdiriterator.cpp 0 |
| 24 | |
| 25 | Here's how to find and read all files filtered by name, recursively: |
| 26 | |
| 27 | \snippet code/src_corelib_io_qdiriterator.cpp 1 |
| 28 | |
| 29 | The next() and nextFileInfo() functions advance the iterator and return |
| 30 | the path or the QFileInfo of the next directory entry. You can also call |
| 31 | filePath() or fileInfo() to get the current file path or QFileInfo without |
| 32 | first advancing the iterator. The fileName() function returns only the |
| 33 | name of the file, similar to how QDir::entryList() works. |
| 34 | |
| 35 | Unlike Qt's container iterators, QDirIterator is uni-directional (i.e., |
| 36 | you cannot iterate directories in reverse order) and does not allow random |
| 37 | access. |
| 38 | |
| 39 | \note This class is deprecated and may be removed in a Qt release. Use |
| 40 | QDirListing instead, see \l {Porting QDirIterator to QDirListing}. |
| 41 | |
| 42 | \sa QDir, QDir::entryList() |
| 43 | */ |
| 44 | |
| 45 | /*! \enum QDirIterator::IteratorFlag |
| 46 | |
| 47 | This enum describes flags that you can combine to configure the behavior |
| 48 | of QDirIterator. |
| 49 | |
| 50 | \value NoIteratorFlags The default value, representing no flags. The |
| 51 | iterator will return entries for the assigned path. |
| 52 | |
| 53 | \value Subdirectories List entries inside all subdirectories as well. |
| 54 | |
| 55 | \value FollowSymlinks When combined with Subdirectories, this flag |
| 56 | enables iterating through all subdirectories of the assigned path, |
| 57 | following all symbolic links. Symbolic link loops (e.g., "link" => "." or |
| 58 | "link" => "..") are automatically detected and ignored. |
| 59 | */ |
| 60 | |
| 61 | #include "qdiriterator.h" |
| 62 | #include "qdir_p.h" |
| 63 | #include "qabstractfileengine_p.h" |
| 64 | #include "qdirlisting.h" |
| 65 | #include "qdirentryinfo_p.h" |
| 66 | |
| 67 | #include <QtCore/qset.h> |
| 68 | #include <QtCore/qstack.h> |
| 69 | #include <QtCore/qvariant.h> |
| 70 | #if QT_CONFIG(regularexpression) |
| 71 | #include <QtCore/qregularexpression.h> |
| 72 | #endif |
| 73 | |
| 74 | #include <QtCore/private/qfilesystemiterator_p.h> |
| 75 | #include <QtCore/private/qfilesystementry_p.h> |
| 76 | #include <QtCore/private/qfilesystemmetadata_p.h> |
| 77 | #include <QtCore/private/qfilesystemengine_p.h> |
| 78 | #include <QtCore/private/qfileinfo_p.h> |
| 79 | #include <QtCore/private/qduplicatetracker_p.h> |
| 80 | |
| 81 | #include <memory> |
| 82 | #include <stack> |
| 83 | #include <vector> |
| 84 | |
| 85 | QT_BEGIN_NAMESPACE |
| 86 | |
| 87 | using namespace Qt::StringLiterals; |
| 88 | |
| 89 | class QDirIteratorPrivate |
| 90 | { |
| 91 | public: |
| 92 | QDirIteratorPrivate(const QString &path, const QStringList &nameFilters = {}, |
| 93 | QDir::Filters filters = QDir::NoFilter, |
| 94 | QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags) |
| 95 | : lister(path, nameFilters, filters.toInt(), flags.toInt()) |
| 96 | { init(); } |
| 97 | |
| 98 | void init() |
| 99 | { |
| 100 | it = lister.begin(); |
| 101 | if (it != lister.end()) |
| 102 | nextFileInfo = it->fileInfo(); |
| 103 | } |
| 104 | |
| 105 | void advance() |
| 106 | { |
| 107 | // Match the behavior of advance() from before porting to QDirListing, |
| 108 | // that is, even if hasNext() returns false, calling next() returns an |
| 109 | // empty string without crashing. QTBUG-130142 |
| 110 | if (it == lister.end()) { |
| 111 | currentFileInfo = {}; |
| 112 | return; |
| 113 | } |
| 114 | currentFileInfo = nextFileInfo; |
| 115 | if (++it != lister.end()) { |
| 116 | nextFileInfo = it->fileInfo(); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | QDirListing lister; |
| 121 | QDirListing::const_iterator it = {}; |
| 122 | QFileInfo currentFileInfo; |
| 123 | QFileInfo nextFileInfo; |
| 124 | }; |
| 125 | |
| 126 | /*! |
| 127 | Constructs a QDirIterator that can iterate over \a dir's entrylist, using |
| 128 | \a dir's name filters and regular filters. You can pass options via \a |
| 129 | flags to decide how the directory should be iterated. |
| 130 | |
| 131 | By default, \a flags is NoIteratorFlags, which provides the same behavior |
| 132 | as in QDir::entryList(). |
| 133 | |
| 134 | The sorting in \a dir is ignored. |
| 135 | |
| 136 | \note To list symlinks that point to non existing files, QDir::System must be |
| 137 | passed to the flags. |
| 138 | |
| 139 | \sa hasNext(), next(), IteratorFlags |
| 140 | */ |
| 141 | QDirIterator::QDirIterator(const QDir &dir, IteratorFlags flags) |
| 142 | : d(new QDirIteratorPrivate(dir.path(), dir.nameFilters(), dir.filter(), flags)) |
| 143 | { |
| 144 | } |
| 145 | |
| 146 | /*! |
| 147 | Constructs a QDirIterator that can iterate over \a path, with no name |
| 148 | filtering and \a filters for entry filtering. You can pass options via \a |
| 149 | flags to decide how the directory should be iterated. |
| 150 | |
| 151 | By default, \a filters is QDir::NoFilter, and \a flags is NoIteratorFlags, |
| 152 | which provides the same behavior as in QDir::entryList(). |
| 153 | |
| 154 | \note To list symlinks that point to non existing files, QDir::System must be |
| 155 | passed to the flags. |
| 156 | |
| 157 | \sa hasNext(), next(), IteratorFlags |
| 158 | */ |
| 159 | QDirIterator::QDirIterator(const QString &path, QDir::Filters filters, IteratorFlags flags) |
| 160 | : d(new QDirIteratorPrivate(path, {}, filters, flags)) |
| 161 | { |
| 162 | } |
| 163 | |
| 164 | /*! |
| 165 | Constructs a QDirIterator that can iterate over \a path. You can pass |
| 166 | options via \a flags to decide how the directory should be iterated. |
| 167 | |
| 168 | By default, \a flags is NoIteratorFlags, which provides the same behavior |
| 169 | as in QDir::entryList(). |
| 170 | |
| 171 | \note To list symlinks that point to non existing files, QDir::System must be |
| 172 | passed to the flags. |
| 173 | |
| 174 | \sa hasNext(), next(), IteratorFlags |
| 175 | */ |
| 176 | QDirIterator::QDirIterator(const QString &path, IteratorFlags flags) |
| 177 | : d(new QDirIteratorPrivate(path, {}, QDir::NoFilter, flags)) |
| 178 | { |
| 179 | } |
| 180 | |
| 181 | /*! |
| 182 | Constructs a QDirIterator that can iterate over \a path, using \a |
| 183 | nameFilters and \a filters. You can pass options via \a flags to decide |
| 184 | how the directory should be iterated. |
| 185 | |
| 186 | By default, \a flags is NoIteratorFlags, which provides the same behavior |
| 187 | as QDir::entryList(). |
| 188 | |
| 189 | For example, the following iterator could be used to iterate over audio |
| 190 | files: |
| 191 | |
| 192 | \snippet code/src_corelib_io_qdiriterator.cpp 2 |
| 193 | |
| 194 | \note To list symlinks that point to non existing files, QDir::System must be |
| 195 | passed to the flags. |
| 196 | |
| 197 | \sa hasNext(), next(), IteratorFlags, QDir::setNameFilters() |
| 198 | */ |
| 199 | QDirIterator::QDirIterator(const QString &path, const QStringList &nameFilters, |
| 200 | QDir::Filters filters, IteratorFlags flags) |
| 201 | : d(new QDirIteratorPrivate(path, nameFilters, filters, flags)) |
| 202 | { |
| 203 | } |
| 204 | |
| 205 | /*! |
| 206 | Destroys the QDirIterator. |
| 207 | */ |
| 208 | QDirIterator::~QDirIterator() |
| 209 | { |
| 210 | } |
| 211 | |
| 212 | /*! |
| 213 | Advances the iterator to the next entry, and returns the file path of this |
| 214 | new entry. If hasNext() returns \c false, this function does nothing, and |
| 215 | returns an empty QString. Ideally you should always call hasNext() before |
| 216 | calling this method. |
| 217 | |
| 218 | You can call fileName() or filePath() to get the current entry's file name |
| 219 | or path, or fileInfo() to get a QFileInfo for the current entry. |
| 220 | |
| 221 | Call nextFileInfo() instead of next() if you're interested in the QFileInfo. |
| 222 | |
| 223 | \sa hasNext(), nextFileInfo(), fileName(), filePath(), fileInfo() |
| 224 | */ |
| 225 | QString QDirIterator::next() |
| 226 | { |
| 227 | d->advance(); |
| 228 | return d->currentFileInfo.filePath(); |
| 229 | } |
| 230 | |
| 231 | /*! |
| 232 | \since 6.3 |
| 233 | |
| 234 | Advances the iterator to the next entry, and returns the file info of this |
| 235 | new entry. If hasNext() returns \c false, this function does nothing, and |
| 236 | returns an empty QFileInfo. Ideally you should always call hasNext() before |
| 237 | calling this method. |
| 238 | |
| 239 | You can call fileName() or filePath() to get the current entry's file name |
| 240 | or path, or fileInfo() to get a QFileInfo for the current entry. |
| 241 | |
| 242 | Call next() instead of nextFileInfo() when all you need is the filePath(). |
| 243 | |
| 244 | \sa hasNext(), fileName(), filePath(), fileInfo() |
| 245 | */ |
| 246 | QFileInfo QDirIterator::nextFileInfo() |
| 247 | { |
| 248 | d->advance(); |
| 249 | return d->currentFileInfo; |
| 250 | } |
| 251 | |
| 252 | /*! |
| 253 | Returns \c true if there is at least one more entry in the directory; |
| 254 | otherwise, false is returned. |
| 255 | |
| 256 | \sa next(), nextFileInfo(), fileName(), filePath(), fileInfo() |
| 257 | */ |
| 258 | bool QDirIterator::hasNext() const |
| 259 | { |
| 260 | return d->it != d->lister.end(); |
| 261 | } |
| 262 | |
| 263 | /*! |
| 264 | Returns the file name for the current directory entry, without the path |
| 265 | prepended. |
| 266 | |
| 267 | This function is convenient when iterating a single directory. When using |
| 268 | the QDirIterator::Subdirectories flag, you can use filePath() to get the |
| 269 | full path. |
| 270 | |
| 271 | \sa filePath(), fileInfo() |
| 272 | */ |
| 273 | QString QDirIterator::fileName() const |
| 274 | { |
| 275 | return d->currentFileInfo.fileName(); |
| 276 | } |
| 277 | |
| 278 | /*! |
| 279 | Returns the full file path for the current directory entry. |
| 280 | |
| 281 | \sa fileInfo(), fileName() |
| 282 | */ |
| 283 | QString QDirIterator::filePath() const |
| 284 | { |
| 285 | return d->currentFileInfo.filePath(); |
| 286 | } |
| 287 | |
| 288 | /*! |
| 289 | Returns a QFileInfo for the current directory entry. |
| 290 | |
| 291 | \sa filePath(), fileName() |
| 292 | */ |
| 293 | QFileInfo QDirIterator::fileInfo() const |
| 294 | { |
| 295 | return d->currentFileInfo; |
| 296 | } |
| 297 | |
| 298 | /*! |
| 299 | Returns the base directory of the iterator. |
| 300 | */ |
| 301 | QString QDirIterator::path() const |
| 302 | { |
| 303 | return d->lister.iteratorPath(); |
| 304 | } |
| 305 | |
| 306 | QT_END_NAMESPACE |
| 307 | |