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 | |
4 | #include "qplatformdefs.h" |
5 | #include "qfilesystemiterator_p.h" |
6 | |
7 | #ifndef QT_NO_FILESYSTEMITERATOR |
8 | |
9 | #include <qvarlengtharray.h> |
10 | |
11 | #include <memory> |
12 | |
13 | #include <stdlib.h> |
14 | #include <errno.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | /* |
19 | Native filesystem iterator, which uses ::opendir()/readdir()/dirent from the system |
20 | libraries to iterate over the directory represented by \a entry. |
21 | */ |
22 | QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry) |
23 | : dirPath(entry.filePath()), |
24 | toUtf16(QStringDecoder::Utf8) |
25 | { |
26 | dir.reset(QT_OPENDIR(name: entry.nativeFilePath().constData())); |
27 | if (!dir) { |
28 | lastError = errno; |
29 | } else { |
30 | if (!dirPath.endsWith(c: u'/')) |
31 | dirPath.append(c: u'/'); |
32 | } |
33 | } |
34 | |
35 | QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDirListing::IteratorFlags) |
36 | : QFileSystemIterator(entry) |
37 | {} |
38 | |
39 | QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters) |
40 | : QFileSystemIterator(entry) |
41 | { |
42 | } |
43 | |
44 | QFileSystemIterator::~QFileSystemIterator() = default; |
45 | |
46 | bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData) |
47 | { |
48 | auto asFileEntry = [this](QStringView name) { |
49 | #ifdef Q_OS_DARWIN |
50 | // must match QFile::decodeName |
51 | QString normalized = name.toString().normalized(QString::NormalizationForm_C); |
52 | name = normalized; |
53 | #endif |
54 | return QFileSystemEntry(dirPath + name, QFileSystemEntry::FromInternalPath()); |
55 | }; |
56 | if (!dir) |
57 | return false; |
58 | |
59 | for (;;) { |
60 | // From readdir man page: |
61 | // If the end of the directory stream is reached, NULL is returned and errno is |
62 | // not changed. If an error occurs, NULL is returned and errno is set to indicate |
63 | // the error. To distinguish end of stream from an error, set errno to zero before |
64 | // calling readdir() and then check the value of errno if NULL is returned. |
65 | errno = 0; |
66 | dirEntry = QT_READDIR(dirp: dir.get()); |
67 | |
68 | if (dirEntry) { |
69 | // POSIX allows readdir() to return a file name in struct dirent that |
70 | // extends past the end of the d_name array (it's a char[1] array on QNX, for |
71 | // example). Therefore, we *must* call strlen() on it to get the actual length |
72 | // of the file name. See: |
73 | // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/dirent.h.html#tag_13_07_05 |
74 | QByteArrayView name(dirEntry->d_name, strlen(s: dirEntry->d_name)); |
75 | // name.size() is sufficient here, see QUtf8::convertToUnicode() for details |
76 | QVarLengthArray<char16_t> buffer(name.size()); |
77 | auto *end = toUtf16.appendToBuffer(out: buffer.data(), ba: name); |
78 | buffer.resize(sz: end - buffer.constData()); |
79 | if (!toUtf16.hasError()) { |
80 | fileEntry = asFileEntry(buffer); |
81 | metaData.fillFromDirEnt(statBuffer: *dirEntry); |
82 | return true; |
83 | } else { |
84 | errno = EILSEQ; // Invalid or incomplete multibyte or wide character |
85 | } |
86 | } else { |
87 | break; |
88 | } |
89 | } |
90 | |
91 | lastError = errno; |
92 | return false; |
93 | } |
94 | |
95 | QT_END_NAMESPACE |
96 | |
97 | #endif // QT_NO_FILESYSTEMITERATOR |
98 | |