diff options
Diffstat (limited to 'src/corelib/io/qdir.cpp')
-rw-r--r-- | src/corelib/io/qdir.cpp | 841 |
1 files changed, 434 insertions, 407 deletions
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 76336ef384..9291201d88 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qplatformdefs.h" #include "qdir.h" @@ -45,7 +9,7 @@ #ifndef QT_NO_DEBUG_STREAM #include "qdebug.h" #endif -#include "qdiriterator.h" +#include "qdirlisting.h" #include "qdatetime.h" #include "qstring.h" #if QT_CONFIG(regularexpression) @@ -57,9 +21,10 @@ #include "qfilesystemengine_p.h" #include <qstringbuilder.h> -#ifdef QT_BUILD_CORE_LIB -# include "qresource.h" -# include "private/qcoreglobaldata_p.h" +#ifndef QT_BOOTSTRAPPED +# include <qcollator.h> +# include "qreadwritelock.h" +# include "qmutex.h" #endif #include <algorithm> @@ -68,6 +33,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + #if defined(Q_OS_WIN) static QString driveSpec(const QString &path) { @@ -91,67 +58,65 @@ enum { }; // Return the length of the root part of an absolute path, for use by cleanPath(), cd(). -static int rootLength(const QString &name, bool allowUncPaths) +static qsizetype rootLength(QStringView name, bool allowUncPaths) { - const int len = name.length(); + const qsizetype len = name.size(); // starts with double slash - if (allowUncPaths && name.startsWith(QLatin1String("//"))) { + if (allowUncPaths && name.startsWith("//"_L1)) { // Server name '//server/path' is part of the prefix. - const int nextSlash = name.indexOf(QLatin1Char('/'), 2); + const qsizetype nextSlash = name.indexOf(u'/', 2); return nextSlash >= 0 ? nextSlash + 1 : len; } #if defined(Q_OS_WIN) - if (len >= 2 && name.at(1) == QLatin1Char(':')) { + if (len >= 2 && name.at(1) == u':') { // Handle a possible drive letter - return len > 2 && name.at(2) == QLatin1Char('/') ? 3 : 2; + return len > 2 && name.at(2) == u'/' ? 3 : 2; } #endif - if (name.at(0) == QLatin1Char('/')) + if (name.at(0) == u'/') return 1; return 0; } //************* QDirPrivate -QDirPrivate::QDirPrivate(const QString &path, const QStringList &nameFilters_, QDir::SortFlags sort_, QDir::Filters filters_) - : QSharedData() - , fileListsInitialized(false) - , nameFilters(nameFilters_) - , sort(sort_) - , filters(filters_) +QDirPrivate::QDirPrivate(const QString &path, const QStringList &nameFilters_, + QDir::SortFlags sort_, QDir::Filters filters_) + : QSharedData(), nameFilters(nameFilters_), sort(sort_), filters(filters_) { setPath(path.isEmpty() ? QString::fromLatin1(".") : path); - bool empty = nameFilters.isEmpty(); - if (!empty) { - empty = true; - for (int i = 0; i < nameFilters.size(); ++i) { - if (!nameFilters.at(i).isEmpty()) { - empty = false; - break; - } - } - } + auto isEmpty = [](const auto &e) { return e.isEmpty(); }; + const bool empty = std::all_of(nameFilters.cbegin(), nameFilters.cend(), isEmpty); if (empty) nameFilters = QStringList(QString::fromLatin1("*")); } QDirPrivate::QDirPrivate(const QDirPrivate ©) - : QSharedData(copy) - , fileListsInitialized(false) - , nameFilters(copy.nameFilters) - , sort(copy.sort) - , filters(copy.filters) - , dirEntry(copy.dirEntry) - , metaData(copy.metaData) -{ + : QSharedData(copy), + // mutex is not copied + nameFilters(copy.nameFilters), + sort(copy.sort), + filters(copy.filters), + // fileEngine is not copied + dirEntry(copy.dirEntry) +{ + QMutexLocker locker(©.fileCache.mutex); + fileCache.fileListsInitialized = copy.fileCache.fileListsInitialized.load(); + fileCache.files = copy.fileCache.files; + fileCache.fileInfos = copy.fileCache.fileInfos; + fileCache.absoluteDirEntry = copy.fileCache.absoluteDirEntry; + fileCache.metaData = copy.fileCache.metaData; } bool QDirPrivate::exists() const { if (!fileEngine) { - QFileSystemEngine::fillMetaData(dirEntry, metaData, - QFileSystemMetaData::ExistsAttribute | QFileSystemMetaData::DirectoryType); // always stat - return metaData.exists() && metaData.isDirectory(); + QMutexLocker locker(&fileCache.mutex); + QFileSystemEngine::fillMetaData( + dirEntry, fileCache.metaData, + QFileSystemMetaData::ExistsAttribute + | QFileSystemMetaData::DirectoryType); // always stat + return fileCache.metaData.exists() && fileCache.metaData.isDirectory(); } const QAbstractFileEngine::FileFlags info = fileEngine->fileFlags(QAbstractFileEngine::DirectoryType @@ -165,10 +130,10 @@ bool QDirPrivate::exists() const // static inline QChar QDirPrivate::getFilterSepChar(const QString &nameFilter) { - QChar sep(QLatin1Char(';')); - int i = nameFilter.indexOf(sep, 0); - if (i == -1 && nameFilter.indexOf(QLatin1Char(' '), 0) != -1) - sep = QChar(QLatin1Char(' ')); + QChar sep(u';'); + qsizetype i = nameFilter.indexOf(sep, 0); + if (i == -1 && nameFilter.indexOf(u' ', 0) != -1) + sep = QChar(u' '); return sep; } @@ -177,10 +142,8 @@ inline QStringList QDirPrivate::splitFilters(const QString &nameFilter, QChar se { if (sep.isNull()) sep = getFilterSepChar(nameFilter); - const auto split = QStringView{nameFilter}.split(sep); QStringList ret; - ret.reserve(split.size()); - for (const auto &e : split) + for (auto e : qTokenize(nameFilter, sep)) ret.append(e.trimmed().toString()); return ret; } @@ -188,64 +151,97 @@ inline QStringList QDirPrivate::splitFilters(const QString &nameFilter, QChar se inline void QDirPrivate::setPath(const QString &path) { QString p = QDir::fromNativeSeparators(path); - if (p.endsWith(QLatin1Char('/')) - && p.length() > 1 + if (p.endsWith(u'/') + && p.size() > 1 #if defined(Q_OS_WIN) && (!(p.length() == 3 && p.at(1).unicode() == ':' && p.at(0).isLetter())) #endif ) { - p.truncate(p.length() - 1); + p.truncate(p.size() - 1); } - dirEntry = QFileSystemEntry(p, QFileSystemEntry::FromInternalPath()); - metaData.clear(); - initFileEngine(); - clearFileLists(); - absoluteDirEntry = QFileSystemEntry(); + clearCache(IncludingMetaData); + fileCache.absoluteDirEntry = QFileSystemEntry(); } -inline void QDirPrivate::clearFileLists() +inline QString QDirPrivate::resolveAbsoluteEntry() const { - fileListsInitialized = false; - files.clear(); - fileInfos.clear(); -} + QMutexLocker locker(&fileCache.mutex); + if (!fileCache.absoluteDirEntry.isEmpty()) + return fileCache.absoluteDirEntry.filePath(); -inline void QDirPrivate::resolveAbsoluteEntry() const -{ - if (!absoluteDirEntry.isEmpty() || dirEntry.isEmpty()) - return; + if (dirEntry.isEmpty()) + return dirEntry.filePath(); QString absoluteName; if (!fileEngine) { if (!dirEntry.isRelative() && dirEntry.isClean()) { - absoluteDirEntry = dirEntry; - return; + fileCache.absoluteDirEntry = dirEntry; + return dirEntry.filePath(); } absoluteName = QFileSystemEngine::absoluteName(dirEntry).filePath(); } else { absoluteName = fileEngine->fileName(QAbstractFileEngine::AbsoluteName); } - - absoluteDirEntry = QFileSystemEntry(QDir::cleanPath(absoluteName), QFileSystemEntry::FromInternalPath()); + auto absoluteFileSystemEntry = + QFileSystemEntry(QDir::cleanPath(absoluteName), QFileSystemEntry::FromInternalPath()); + fileCache.absoluteDirEntry = absoluteFileSystemEntry; + return absoluteFileSystemEntry.filePath(); } /* For sorting */ struct QDirSortItem { + QDirSortItem() = default; + QDirSortItem(const QFileInfo &fi, QDir::SortFlags sort) + : item(fi) + { + // A dir e.g. "dirA.bar" doesn't have actually have an extension/suffix, when + // sorting by type such "suffix" should be ignored but that would complicate + // the code and uses can change the behavior by setting DirsFirst/DirsLast + if (sort.testAnyFlag(QDir::Type)) + suffix_cache = item.suffix(); + } + mutable QString filename_cache; - mutable QString suffix_cache; + QString suffix_cache; QFileInfo item; }; - class QDirSortItemComparator { QDir::SortFlags qt_cmp_si_sort_flags; + +#ifndef QT_BOOTSTRAPPED + QCollator *collator = nullptr; +#endif public: - QDirSortItemComparator(QDir::SortFlags flags) : qt_cmp_si_sort_flags(flags) {} +#ifndef QT_BOOTSTRAPPED + QDirSortItemComparator(QDir::SortFlags flags, QCollator *coll = nullptr) + : qt_cmp_si_sort_flags(flags), collator(coll) + { + Q_ASSERT(!qt_cmp_si_sort_flags.testAnyFlag(QDir::LocaleAware) || collator); + + if (collator && qt_cmp_si_sort_flags.testAnyFlag(QDir::IgnoreCase)) + collator->setCaseSensitivity(Qt::CaseInsensitive); + } +#else + QDirSortItemComparator(QDir::SortFlags flags) + : qt_cmp_si_sort_flags(flags) + { + } +#endif bool operator()(const QDirSortItem &, const QDirSortItem &) const; + + int compareStrings(const QString &a, const QString &b, Qt::CaseSensitivity cs) const + { +#ifndef QT_BOOTSTRAPPED + if (collator) + return collator->compare(a, b); +#endif + return a.compare(b, cs); + } }; bool QDirSortItemComparator::operator()(const QDirSortItem &n1, const QDirSortItem &n2) const @@ -258,43 +254,25 @@ bool QDirSortItemComparator::operator()(const QDirSortItem &n1, const QDirSortIt if ((qt_cmp_si_sort_flags & QDir::DirsLast) && (f1->item.isDir() != f2->item.isDir())) return !f1->item.isDir(); + const bool ic = qt_cmp_si_sort_flags.testAnyFlag(QDir::IgnoreCase); + const auto qtcase = ic ? Qt::CaseInsensitive : Qt::CaseSensitive; + qint64 r = 0; int sortBy = ((qt_cmp_si_sort_flags & QDir::SortByMask) | (qt_cmp_si_sort_flags & QDir::Type)).toInt(); switch (sortBy) { case QDir::Time: { - QDateTime firstModified = f1->item.lastModified(); - QDateTime secondModified = f2->item.lastModified(); - - // QDateTime by default will do all sorts of conversions on these to - // find timezones, which is incredibly expensive. As we aren't - // presenting these to the user, we don't care (at all) about the - // local timezone, so force them to UTC to avoid that conversion. - firstModified.setTimeSpec(Qt::UTC); - secondModified.setTimeSpec(Qt::UTC); - + const QDateTime firstModified = f1->item.lastModified(QTimeZone::UTC); + const QDateTime secondModified = f2->item.lastModified(QTimeZone::UTC); r = firstModified.msecsTo(secondModified); break; } case QDir::Size: r = f2->item.size() - f1->item.size(); break; - case QDir::Type: - { - bool ic = qt_cmp_si_sort_flags.testAnyFlag(QDir::IgnoreCase); - - if (f1->suffix_cache.isNull()) - f1->suffix_cache = ic ? f1->item.suffix().toLower() - : f1->item.suffix(); - if (f2->suffix_cache.isNull()) - f2->suffix_cache = ic ? f2->item.suffix().toLower() - : f2->item.suffix(); - - r = qt_cmp_si_sort_flags & QDir::LocaleAware - ? f1->suffix_cache.localeAwareCompare(f2->suffix_cache) - : f1->suffix_cache.compare(f2->suffix_cache); - } + case QDir::Type: + r = compareStrings(f1->suffix_cache, f2->suffix_cache, qtcase); break; default: ; @@ -302,69 +280,89 @@ bool QDirSortItemComparator::operator()(const QDirSortItem &n1, const QDirSortIt if (r == 0 && sortBy != QDir::Unsorted) { // Still not sorted - sort by name - bool ic = qt_cmp_si_sort_flags.testAnyFlag(QDir::IgnoreCase); if (f1->filename_cache.isNull()) - f1->filename_cache = ic ? f1->item.fileName().toLower() - : f1->item.fileName(); + f1->filename_cache = f1->item.fileName(); if (f2->filename_cache.isNull()) - f2->filename_cache = ic ? f2->item.fileName().toLower() - : f2->item.fileName(); + f2->filename_cache = f2->item.fileName(); - r = qt_cmp_si_sort_flags & QDir::LocaleAware - ? f1->filename_cache.localeAwareCompare(f2->filename_cache) - : f1->filename_cache.compare(f2->filename_cache); + r = compareStrings(f1->filename_cache, f2->filename_cache, qtcase); } if (qt_cmp_si_sort_flags & QDir::Reversed) return r > 0; return r < 0; } -inline void QDirPrivate::sortFileList(QDir::SortFlags sort, QFileInfoList &l, +inline void QDirPrivate::sortFileList(QDir::SortFlags sort, const QFileInfoList &l, QStringList *names, QFileInfoList *infos) { - // names and infos are always empty lists or 0 here - int n = l.size(); - if (n > 0) { - if (n == 1 || (sort & QDir::SortByMask) == QDir::Unsorted) { - if (infos) - *infos = l; - if (names) { - for (int i = 0; i < n; ++i) - names->append(l.at(i).fileName()); - } + Q_ASSERT(names || infos); + Q_ASSERT(!infos || infos->isEmpty()); + Q_ASSERT(!names || names->isEmpty()); + + const qsizetype n = l.size(); + if (n == 0) + return; + + if (n == 1 || (sort & QDir::SortByMask) == QDir::Unsorted) { + if (infos) + *infos = l; + + if (names) { + for (const QFileInfo &fi : l) + names->append(fi.fileName()); + } + } else { + QScopedArrayPointer<QDirSortItem> si(new QDirSortItem[n]); + for (qsizetype i = 0; i < n; ++i) + si[i] = QDirSortItem{l.at(i), sort}; + +#ifndef QT_BOOTSTRAPPED + if (sort.testAnyFlag(QDir::LocaleAware)) { + QCollator coll; + std::sort(si.data(), si.data() + n, QDirSortItemComparator(sort, &coll)); } else { - QScopedArrayPointer<QDirSortItem> si(new QDirSortItem[n]); - for (int i = 0; i < n; ++i) - si[i].item = l.at(i); std::sort(si.data(), si.data() + n, QDirSortItemComparator(sort)); - // put them back in the list(s) - if (infos) { - for (int i = 0; i < n; ++i) - infos->append(si[i].item); - } + } +#else + std::sort(si.data(), si.data() + n, QDirSortItemComparator(sort)); +#endif // QT_BOOTSTRAPPED + + // put them back in the list(s) + for (qsizetype i = 0; i < n; ++i) { + auto &fileInfo = si[i].item; + if (infos) + infos->append(fileInfo); if (names) { - for (int i = 0; i < n; ++i) - names->append(si[i].item.fileName()); + const bool cached = !si[i].filename_cache.isNull(); + names->append(cached ? si[i].filename_cache : fileInfo.fileName()); } } } } + inline void QDirPrivate::initFileLists(const QDir &dir) const { - if (!fileListsInitialized) { + QMutexLocker locker(&fileCache.mutex); + if (!fileCache.fileListsInitialized) { QFileInfoList l; - QDirIterator it(dir); - while (it.hasNext()) - l.append(it.nextFileInfo()); - sortFileList(sort, l, &files, &fileInfos); - fileListsInitialized = true; + for (const auto &dirEntry : QDirListing(dir)) + l.emplace_back(dirEntry.fileInfo()); + + sortFileList(sort, l, &fileCache.files, &fileCache.fileInfos); + fileCache.fileListsInitialized = true; } } -inline void QDirPrivate::initFileEngine() +inline void QDirPrivate::clearCache(MetaDataClearing mode) { - fileEngine.reset(QFileSystemEngine::resolveEntryAndCreateLegacyEngine(dirEntry, metaData)); + QMutexLocker locker(&fileCache.mutex); + if (mode == IncludingMetaData) + fileCache.metaData.clear(); + fileCache.fileListsInitialized = false; + fileCache.files.clear(); + fileCache.fileInfos.clear(); + fileEngine = QFileSystemEngine::createLegacyEngine(dirEntry, fileCache.metaData); } /*! @@ -376,6 +374,7 @@ inline void QDirPrivate::initFileEngine() \ingroup shared \reentrant + \compares equality A QDir is used to manipulate path names, access information regarding paths and files, and manipulate the underlying file @@ -397,7 +396,7 @@ inline void QDirPrivate::initFileEngine() \snippet code/src_corelib_io_qdir.cpp 0 On Windows, the second example above will be translated to - \c{C:\Documents and Settings} when used to access files. + \c{C:\Users} when used to access files. Examples of relative paths: @@ -531,8 +530,8 @@ inline void QDirPrivate::initFileEngine() \snippet code/src_corelib_io_qdir.cpp 4 - (We could also use the static convenience function - QFile::exists().) + (We could also use one of the static convenience functions + QFileInfo::exists() or QFile::exists().) Traversing directories and reading a file: @@ -543,7 +542,12 @@ inline void QDirPrivate::initFileEngine() \snippet qdir-listfiles/main.cpp 0 - \sa QFileInfo, QFile, QFileDialog, QCoreApplication::applicationDirPath(), {Find Files Example} + \section1 Platform Specific Issues + + \include android-content-uri-limitations.qdocinc + + \sa QFileInfo, QFile, QFileDialog, QCoreApplication::applicationDirPath(), + {Fetch More Example} */ /*! @@ -647,7 +651,7 @@ void QDir::setPath(const QString &path) */ QString QDir::path() const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); return d->dirEntry.filePath(); } @@ -661,9 +665,11 @@ QString QDir::path() const */ QString QDir::absolutePath() const { - const QDirPrivate* d = d_ptr.constData(); - d->resolveAbsoluteEntry(); - return d->absoluteDirEntry.filePath(); + Q_D(const QDir); + if (!d->fileEngine) + return d->resolveAbsoluteEntry(); + + return d->fileEngine->fileName(QAbstractFileEngine::AbsoluteName); } /*! @@ -684,9 +690,11 @@ QString QDir::absolutePath() const */ QString QDir::canonicalPath() const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); if (!d->fileEngine) { - QFileSystemEntry answer = QFileSystemEngine::canonicalName(d->dirEntry, d->metaData); + QMutexLocker locker(&d->fileCache.mutex); + QFileSystemEntry answer = + QFileSystemEngine::canonicalName(d->dirEntry, d->fileCache.metaData); return answer.filePath(); } return d->fileEngine->fileName(QAbstractFileEngine::CanonicalName); @@ -705,29 +713,31 @@ QString QDir::canonicalPath() const */ QString QDir::dirName() const { - const QDirPrivate* d = d_ptr.constData(); - return d->dirEntry.fileName(); + Q_D(const QDir); + if (!d_ptr->fileEngine) + return d->dirEntry.fileName(); + return d->fileEngine->fileName(QAbstractFileEngine::BaseName); } #ifdef Q_OS_WIN -static int drivePrefixLength(const QString &path) +static qsizetype drivePrefixLength(QStringView path) { // Used to extract path's drive for use as prefix for an "absolute except for drive" path - const int size = path.length(); - int drive = 2; // length of drive prefix + const qsizetype size = path.size(); + qsizetype drive = 2; // length of drive prefix if (size > 1 && path.at(1).unicode() == ':') { if (Q_UNLIKELY(!path.at(0).isLetter())) return 0; - } else if (path.startsWith(QLatin1String("//"))) { + } else if (path.startsWith("//"_L1)) { // UNC path; use its //server/share part as "drive" - it's as sane a // thing as we can do. - for (int i = 2; i-- > 0; ) { // Scan two "path fragments": + for (int i = 0 ; i < 2 ; ++i) { // Scan two "path fragments": while (drive < size && path.at(drive).unicode() == '/') drive++; if (drive >= size) { qWarning("Base directory starts with neither a drive nor a UNC share: %s", - qUtf8Printable(QDir::toNativeSeparators(path))); + qUtf8Printable(QDir::toNativeSeparators(path.toString()))); return 0; } while (drive < size && path.at(drive).unicode() != '/') @@ -751,7 +761,7 @@ static bool treatAsAbsolute(const QString &path) // a colon in the path. // FIXME: relies on virtual file-systems having colons in their prefixes. // The case of an MS-absolute C:/... path happens to work either way. - return (path.contains(QLatin1Char(':')) && QFileInfo(path).isAbsolute()) + return (path.contains(u':') && QFileInfo(path).isAbsolute()) || QFileSystemEntry(path).isAbsolute(); } @@ -769,22 +779,22 @@ QString QDir::filePath(const QString &fileName) const if (treatAsAbsolute(fileName)) return fileName; - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); QString ret = d->dirEntry.filePath(); if (fileName.isEmpty()) return ret; #ifdef Q_OS_WIN - if (fileName.startsWith(QLatin1Char('/')) || fileName.startsWith(QLatin1Char('\\'))) { + if (fileName.startsWith(u'/') || fileName.startsWith(u'\\')) { // Handle the "absolute except for drive" case (i.e. \blah not c:\blah): - const int drive = drivePrefixLength(ret); + const qsizetype drive = drivePrefixLength(ret); return drive > 0 ? QStringView{ret}.left(drive) % fileName : fileName; } #endif // Q_OS_WIN - if (ret.isEmpty() || ret.endsWith(QLatin1Char('/'))) + if (ret.isEmpty() || ret.endsWith(u'/')) return ret % fileName; - return ret % QLatin1Char('/') % fileName; + return ret % u'/' % fileName; } /*! @@ -800,16 +810,15 @@ QString QDir::absoluteFilePath(const QString &fileName) const if (treatAsAbsolute(fileName)) return fileName; - const QDirPrivate* d = d_ptr.constData(); - d->resolveAbsoluteEntry(); - const QString absoluteDirPath = d->absoluteDirEntry.filePath(); + Q_D(const QDir); + QString absoluteDirPath = d->resolveAbsoluteEntry(); if (fileName.isEmpty()) return absoluteDirPath; #ifdef Q_OS_WIN // Handle the "absolute except for drive" case (i.e. \blah not c:\blah): - if (fileName.startsWith(QLatin1Char('/')) || fileName.startsWith(QLatin1Char('\\'))) { + if (fileName.startsWith(u'/') || fileName.startsWith(u'\\')) { // Combine absoluteDirPath's drive with fileName - const int drive = drivePrefixLength(absoluteDirPath); + const qsizetype drive = drivePrefixLength(absoluteDirPath); if (Q_LIKELY(drive)) return QStringView{absoluteDirPath}.left(drive) % fileName; @@ -818,8 +827,8 @@ QString QDir::absoluteFilePath(const QString &fileName) const return QString(); } #endif // Q_OS_WIN - if (!absoluteDirPath.endsWith(QLatin1Char('/'))) - return absoluteDirPath % QLatin1Char('/') % fileName; + if (!absoluteDirPath.endsWith(u'/')) + return absoluteDirPath % u'/' % fileName; return absoluteDirPath % fileName; } @@ -849,9 +858,10 @@ QString QDir::relativeFilePath(const QString &fileName) const } if (fileDrive.toLower() != dirDrive.toLower() - || (file.startsWith(QLatin1String("//")) - && !dir.startsWith(QLatin1String("//")))) + || (file.startsWith("//"_L1) + && !dir.startsWith("//"_L1))) { return file; + } dir.remove(0, dirDrive.size()); if (!fileDriveMissing) @@ -859,8 +869,8 @@ QString QDir::relativeFilePath(const QString &fileName) const #endif QString result; - const auto dirElts = dir.tokenize(QLatin1Char('/'), Qt::SkipEmptyParts); - const auto fileElts = file.tokenize(QLatin1Char('/'), Qt::SkipEmptyParts); + const auto dirElts = dir.tokenize(u'/', Qt::SkipEmptyParts); + const auto fileElts = file.tokenize(u'/', Qt::SkipEmptyParts); const auto dend = dirElts.end(); const auto fend = fileElts.end(); @@ -883,20 +893,20 @@ QString QDir::relativeFilePath(const QString &fileName) const } while (dit != dend) { - result += QLatin1String("../"); + result += "../"_L1; ++dit; } if (fit != fend) { while (fit != fend) { result += *fit++; - result += QLatin1Char('/'); + result += u'/'; } result.chop(1); } if (result.isEmpty()) - result = QLatin1String("."); + result = "."_L1; return result; } @@ -918,16 +928,16 @@ QString QDir::relativeFilePath(const QString &fileName) const QString QDir::toNativeSeparators(const QString &pathName) { #if defined(Q_OS_WIN) - int i = pathName.indexOf(QLatin1Char('/')); + qsizetype i = pathName.indexOf(u'/'); if (i != -1) { QString n(pathName); QChar * const data = n.data(); - data[i++] = QLatin1Char('\\'); + data[i++] = u'\\'; for (; i < n.length(); ++i) { - if (data[i] == QLatin1Char('/')) - data[i] = QLatin1Char('\\'); + if (data[i] == u'/') + data[i] = u'\\'; } return n; @@ -975,19 +985,19 @@ bool QDir::cd(const QString &dirName) // Don't detach just yet. const QDirPrivate * const d = d_ptr.constData(); - if (dirName.isEmpty() || dirName == QLatin1String(".")) + if (dirName.isEmpty() || dirName == u'.') return true; QString newPath; if (isAbsolutePath(dirName)) { newPath = qt_cleanPath(dirName); } else { newPath = d->dirEntry.filePath(); - if (!newPath.endsWith(QLatin1Char('/'))) - newPath += QLatin1Char('/'); + if (!newPath.endsWith(u'/')) + newPath += u'/'; newPath += dirName; - if (dirName.indexOf(QLatin1Char('/')) >= 0 - || dirName == QLatin1String("..") - || d->dirEntry.filePath() == QLatin1String(".")) { + if (dirName.indexOf(u'/') >= 0 + || dirName == ".."_L1 + || d->dirEntry.filePath() == u'.') { bool ok; newPath = qt_cleanPath(newPath, &ok); if (!ok) @@ -1000,7 +1010,7 @@ bool QDir::cd(const QString &dirName) while (dir.cdUp()) ; */ - if (newPath.startsWith(QLatin1String(".."))) { + if (newPath.startsWith(".."_L1)) { newPath = QFileInfo(newPath).absoluteFilePath(); } } @@ -1023,6 +1033,9 @@ bool QDir::cd(const QString &dirName) otherwise returns \c false. Note that the logical cdUp() operation is not performed if the new directory does not exist. + \note On Android, this is not supported for content URIs. For more information, + see \l {Android: DocumentFile.getParentFile()}{DocumentFile.getParentFile()}. + \sa cd(), isReadable(), exists(), path() */ bool QDir::cdUp() @@ -1035,7 +1048,7 @@ bool QDir::cdUp() */ QStringList QDir::nameFilters() const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); return d->nameFilters; } @@ -1056,14 +1069,22 @@ QStringList QDir::nameFilters() const */ void QDir::setNameFilters(const QStringList &nameFilters) { - QDirPrivate* d = d_ptr.data(); - d->initFileEngine(); - d->clearFileLists(); - + Q_D(QDir); + d->clearCache(QDirPrivate::KeepMetaData); d->nameFilters = nameFilters; } -#ifdef QT_BUILD_CORE_LIB +#ifndef QT_BOOTSTRAPPED + +namespace { +struct DirSearchPaths { + mutable QReadWriteLock mutex; + QHash<QString, QStringList> paths; +}; +} + +Q_GLOBAL_STATIC(DirSearchPaths, dirSearchPaths) + /*! \since 4.3 @@ -1086,24 +1107,24 @@ void QDir::setNameFilters(const QStringList &nameFilters) */ void QDir::setSearchPaths(const QString &prefix, const QStringList &searchPaths) { - if (prefix.length() < 2) { + if (prefix.size() < 2) { qWarning("QDir::setSearchPaths: Prefix must be longer than 1 character"); return; } - for (int i = 0; i < prefix.count(); ++i) { - if (!prefix.at(i).isLetterOrNumber()) { + for (QChar ch : prefix) { + if (!ch.isLetterOrNumber()) { qWarning("QDir::setSearchPaths: Prefix can only contain letters or numbers"); return; } } - QWriteLocker lock(&QCoreGlobalData::instance()->dirSearchPathsLock); - QMap<QString, QStringList> &paths = QCoreGlobalData::instance()->dirSearchPaths; + DirSearchPaths &conf = *dirSearchPaths; + const QWriteLocker lock(&conf.mutex); if (searchPaths.isEmpty()) { - paths.remove(prefix); + conf.paths.remove(prefix); } else { - paths.insert(prefix, searchPaths); + conf.paths.insert(prefix, searchPaths); } } @@ -1119,8 +1140,9 @@ void QDir::addSearchPath(const QString &prefix, const QString &path) if (path.isEmpty()) return; - QWriteLocker lock(&QCoreGlobalData::instance()->dirSearchPathsLock); - QCoreGlobalData::instance()->dirSearchPaths[prefix] += path; + DirSearchPaths &conf = *dirSearchPaths; + const QWriteLocker lock(&conf.mutex); + conf.paths[prefix] += path; } /*! @@ -1132,18 +1154,22 @@ void QDir::addSearchPath(const QString &prefix, const QString &path) */ QStringList QDir::searchPaths(const QString &prefix) { - QReadLocker lock(&QCoreGlobalData::instance()->dirSearchPathsLock); - return QCoreGlobalData::instance()->dirSearchPaths.value(prefix); + if (!dirSearchPaths.exists()) + return QStringList(); + + const DirSearchPaths &conf = *dirSearchPaths; + const QReadLocker lock(&conf.mutex); + return conf.paths.value(prefix); } -#endif // QT_BUILD_CORE_LIB +#endif // QT_BOOTSTRAPPED /*! Returns the value set by setFilter() */ QDir::Filters QDir::filter() const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); return d->filters; } @@ -1206,9 +1232,9 @@ QDir::Filters QDir::filter() const files that the application can read, write, or execute, and symlinks to such files/directories can be listed. - To retrieve the permissons for a directory, use the + To retrieve the permissions for a directory, use the entryInfoList() function to get the associated QFileInfo objects - and then use the QFileInfo::permissons() to obtain the permissions + and then use the QFileInfo::permissions() to obtain the permissions and ownership for each file. */ @@ -1222,10 +1248,8 @@ QDir::Filters QDir::filter() const */ void QDir::setFilter(Filters filters) { - QDirPrivate* d = d_ptr.data(); - d->initFileEngine(); - d->clearFileLists(); - + Q_D(QDir); + d->clearCache(QDirPrivate::KeepMetaData); d->filters = filters; } @@ -1236,7 +1260,7 @@ void QDir::setFilter(Filters filters) */ QDir::SortFlags QDir::sorting() const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); return d->sort; } @@ -1269,6 +1293,7 @@ QDir::SortFlags QDir::sorting() const after the directories, again in reverse order. */ +#ifndef QT_BOOTSTRAPPED /*! Sets the sort order used by entryList() and entryInfoList(). @@ -1279,10 +1304,8 @@ QDir::SortFlags QDir::sorting() const */ void QDir::setSorting(SortFlags sort) { - QDirPrivate* d = d_ptr.data(); - d->initFileEngine(); - d->clearFileLists(); - + Q_D(QDir); + d->clearCache(QDirPrivate::KeepMetaData); d->sort = sort; } @@ -1291,13 +1314,16 @@ void QDir::setSorting(SortFlags sort) Equivalent to entryList().count(). + \note In Qt versions prior to 6.5, this function returned \c{uint}, not + \c{qsizetype}. + \sa operator[](), entryList() */ -uint QDir::count() const +qsizetype QDir::count(QT6_IMPL_NEW_OVERLOAD) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); d->initFileLists(*this); - return d->files.count(); + return d->fileCache.files.size(); } /*! @@ -1305,13 +1331,15 @@ uint QDir::count() const names. Equivalent to entryList().at(index). \a pos must be a valid index position in the list (i.e., 0 <= pos < count()). + \note In Qt versions prior to 6.5, \a pos was an \c{int}, not \c{qsizetype}. + \sa count(), entryList() */ -QString QDir::operator[](int pos) const +QString QDir::operator[](qsizetype pos) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); d->initFileLists(*this); - return d->files[pos]; + return d->fileCache.files[pos]; } /*! @@ -1335,7 +1363,7 @@ QString QDir::operator[](int pos) const */ QStringList QDir::entryList(Filters filters, SortFlags sort) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); return entryList(d->nameFilters, filters, sort); } @@ -1358,7 +1386,7 @@ QStringList QDir::entryList(Filters filters, SortFlags sort) const */ QFileInfoList QDir::entryInfoList(Filters filters, SortFlags sort) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); return entryInfoList(d->nameFilters, filters, sort); } @@ -1381,24 +1409,34 @@ QFileInfoList QDir::entryInfoList(Filters filters, SortFlags sort) const QStringList QDir::entryList(const QStringList &nameFilters, Filters filters, SortFlags sort) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); if (filters == NoFilter) filters = d->filters; if (sort == NoSort) sort = d->sort; + const bool needsSorting = (sort & QDir::SortByMask) != QDir::Unsorted; + if (filters == d->filters && sort == d->sort && nameFilters == d->nameFilters) { - d->initFileLists(*this); - return d->files; + // Don't fill a QFileInfo cache if we just need names + if (needsSorting || d->fileCache.fileListsInitialized) { + d->initFileLists(*this); + return d->fileCache.files; + } } - QFileInfoList l; - QDirIterator it(d->dirEntry.filePath(), nameFilters, filters); - while (it.hasNext()) - l.append(it.nextFileInfo()); + QDirListing dirList(d->dirEntry.filePath(), nameFilters, filters); QStringList ret; - d->sortFileList(sort, l, &ret, nullptr); + if (needsSorting) { + QFileInfoList l; + for (const auto &dirEntry : dirList) + l.emplace_back(dirEntry.fileInfo()); + d->sortFileList(sort, l, &ret, nullptr); + } else { + for (const auto &dirEntry : dirList) + ret.emplace_back(dirEntry.fileName()); + } return ret; } @@ -1421,7 +1459,7 @@ QStringList QDir::entryList(const QStringList &nameFilters, Filters filters, QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filters, SortFlags sort) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); if (filters == NoFilter) filters = d->filters; @@ -1430,30 +1468,63 @@ QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filter if (filters == d->filters && sort == d->sort && nameFilters == d->nameFilters) { d->initFileLists(*this); - return d->fileInfos; + return d->fileCache.fileInfos; } QFileInfoList l; - QDirIterator it(d->dirEntry.filePath(), nameFilters, filters); - while (it.hasNext()) - l.append(it.nextFileInfo()); + for (const auto &dirEntry : QDirListing(d->dirEntry.filePath(), nameFilters, filters)) + l.emplace_back(dirEntry.fileInfo()); QFileInfoList ret; d->sortFileList(sort, l, nullptr, &ret); return ret; } +#endif // !QT_BOOTSTRAPPED /*! Creates a sub-directory called \a dirName. Returns \c true on success; otherwise returns \c false. - If the directory already exists when this function is called, it will return false. + If the directory already exists when this function is called, it will return \c false. + + The permissions of the created directory are set to \a{permissions}. + + On POSIX systems the permissions are influenced by the value of \c umask. + + On Windows the permissions are emulated using ACLs. These ACLs may be in non-canonical + order when the group is granted less permissions than others. Files and directories with + such permissions will generate warnings when the Security tab of the Properties dialog + is opened. Granting the group all permissions granted to others avoids such warnings. \sa rmdir() + + \since 6.3 +*/ +bool QDir::mkdir(const QString &dirName, QFile::Permissions permissions) const +{ + Q_D(const QDir); + + if (dirName.isEmpty()) { + qWarning("QDir::mkdir: Empty or null file name"); + return false; + } + + QString fn = filePath(dirName); + if (!d->fileEngine) + return QFileSystemEngine::createDirectory(QFileSystemEntry(fn), false, permissions); + return d->fileEngine->mkdir(fn, false, permissions); +} + +/*! + \overload + Creates a sub-directory called \a dirName with default permissions. + + On POSIX systems the default is to grant all permissions allowed by \c umask. + On Windows, the new directory inherits its permissions from its parent directory. */ bool QDir::mkdir(const QString &dirName) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); if (dirName.isEmpty()) { qWarning("QDir::mkdir: Empty or null file name"); @@ -1477,7 +1548,7 @@ bool QDir::mkdir(const QString &dirName) const */ bool QDir::rmdir(const QString &dirName) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); if (dirName.isEmpty()) { qWarning("QDir::rmdir: Empty or null file name"); @@ -1505,7 +1576,7 @@ bool QDir::rmdir(const QString &dirName) const */ bool QDir::mkpath(const QString &dirPath) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); if (dirPath.isEmpty()) { qWarning("QDir::mkpath: Empty or null file name"); @@ -1531,7 +1602,7 @@ bool QDir::mkpath(const QString &dirPath) const */ bool QDir::rmpath(const QString &dirPath) const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); if (dirPath.isEmpty()) { qWarning("QDir::rmpath: Empty or null file name"); @@ -1544,6 +1615,7 @@ bool QDir::rmpath(const QString &dirPath) const return d->fileEngine->rmdir(fn, true); } +#ifndef QT_BOOTSTRAPPED /*! \since 5.0 Removes the directory, including all its contents. @@ -1572,12 +1644,11 @@ bool QDir::removeRecursively() bool success = true; const QString dirPath = path(); // not empty -- we must empty it first - QDirIterator di(dirPath, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot); - while (di.hasNext()) { - const QFileInfo fi = di.nextFileInfo(); - const QString &filePath = di.filePath(); + constexpr auto dirFilters = QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot; + for (const auto &dirEntry : QDirListing(dirPath, dirFilters)) { + const QString &filePath = dirEntry.filePath(); bool ok; - if (fi.isDir() && !fi.isSymLink()) { + if (dirEntry.isDir() && !dirEntry.isSymLink()) { ok = QDir(filePath).removeRecursively(); // recursive } else { ok = QFile::remove(filePath); @@ -1597,6 +1668,7 @@ bool QDir::removeRecursively() return success; } +#endif // !QT_BOOTSTRAPPED /*! Returns \c true if the directory is readable \e and we can open files @@ -1609,13 +1681,15 @@ bool QDir::removeRecursively() */ bool QDir::isReadable() const { - const QDirPrivate* d = d_ptr.constData(); + Q_D(const QDir); if (!d->fileEngine) { - if (!d->metaData.hasFlags(QFileSystemMetaData::UserReadPermission)) - QFileSystemEngine::fillMetaData(d->dirEntry, d->metaData, QFileSystemMetaData::UserReadPermission); - - return d->metaData.permissions().testAnyFlag(QFile::ReadUser); + QMutexLocker locker(&d->fileCache.mutex); + if (!d->fileCache.metaData.hasFlags(QFileSystemMetaData::UserReadPermission)) { + QFileSystemEngine::fillMetaData(d->dirEntry, d->fileCache.metaData, + QFileSystemMetaData::UserReadPermission); + } + return d->fileCache.metaData.permissions().testAnyFlag(QFile::ReadUser); } const QAbstractFileEngine::FileFlags info = @@ -1712,7 +1786,7 @@ bool QDir::isRelative() const */ bool QDir::makeAbsolute() { - const QDirPrivate *d = d_ptr.constData(); + Q_D(const QDir); std::unique_ptr<QDirPrivate> dir; if (!!d->fileEngine) { QString absolutePath = d->fileEngine->fileName(QAbstractFileEngine::AbsoluteName); @@ -1722,16 +1796,18 @@ bool QDir::makeAbsolute() dir.reset(new QDirPrivate(*d_ptr.constData())); dir->setPath(absolutePath); } else { // native FS - d->resolveAbsoluteEntry(); + QString absoluteFilePath = d->resolveAbsoluteEntry(); dir.reset(new QDirPrivate(*d_ptr.constData())); - dir->setPath(d->absoluteDirEntry.filePath()); + dir->setPath(absoluteFilePath); } d_ptr = dir.release(); // actually detach return true; } /*! - Returns \c true if directory \a dir and this directory have the same + \fn bool QDir::operator==(const QDir &lhs, const QDir &rhs) + + Returns \c true if directory \a lhs and directory \a rhs have the same path and their sort and filter settings are the same; otherwise returns \c false. @@ -1739,10 +1815,10 @@ bool QDir::makeAbsolute() \snippet code/src_corelib_io_qdir.cpp 10 */ -bool QDir::operator==(const QDir &dir) const +bool comparesEqual(const QDir &lhs, const QDir &rhs) { - const QDirPrivate *d = d_ptr.constData(); - const QDirPrivate *other = dir.d_ptr.constData(); + const QDirPrivate *d = lhs.d_ptr.constData(); + const QDirPrivate *other = rhs.d_ptr.constData(); if (d == other) return true; @@ -1766,18 +1842,18 @@ bool QDir::operator==(const QDir &dir) const if (d->dirEntry.filePath() == other->dirEntry.filePath()) return true; - if (exists()) { - if (!dir.exists()) + if (lhs.exists()) { + if (!rhs.exists()) return false; //can't be equal if only one exists // Both exist, fallback to expensive canonical path computation - return canonicalPath().compare(dir.canonicalPath(), sensitive) == 0; + return lhs.canonicalPath().compare(rhs.canonicalPath(), sensitive) == 0; } else { - if (dir.exists()) + if (rhs.exists()) return false; //can't be equal if only one exists // Neither exists, compare absolute paths rather than canonical (which would be empty strings) - d->resolveAbsoluteEntry(); - other->resolveAbsoluteEntry(); - return d->absoluteDirEntry.filePath().compare(other->absoluteDirEntry.filePath(), sensitive) == 0; + QString thisFilePath = d->resolveAbsoluteEntry(); + QString otherFilePath = other->resolveAbsoluteEntry(); + return thisFilePath.compare(otherFilePath, sensitive) == 0; } } return false; @@ -1802,11 +1878,10 @@ QDir &QDir::operator=(const QDir &dir) */ /*! - \fn bool QDir::operator!=(const QDir &dir) const + \fn bool QDir::operator!=(const QDir &lhs, const QDir &rhs) - Returns \c true if directory \a dir and this directory have different - paths or different sort or filter settings; otherwise returns - false. + Returns \c true if directory \a lhs and directory \a rhs have different + paths or different sort or filter settings; otherwise returns \c false. Example: @@ -1873,9 +1948,10 @@ bool QDir::exists(const QString &name) const qWarning("QDir::exists: Empty or null file name"); return false; } - return QFile::exists(filePath(name)); + return QFileInfo::exists(filePath(name)); } +#ifndef QT_BOOTSTRAPPED /*! Returns whether the directory is empty. @@ -1891,16 +1967,18 @@ bool QDir::exists(const QString &name) const */ bool QDir::isEmpty(Filters filters) const { - const auto d = d_ptr.constData(); - QDirIterator it(d->dirEntry.filePath(), d->nameFilters, filters); - return !it.hasNext(); + Q_D(const QDir); + QDirListing dirList(d->dirEntry.filePath(), d->nameFilters, filters); + return dirList.cbegin() == dirList.cend(); } +#endif // !QT_BOOTSTRAPPED /*! Returns a list of the root directories on this system. On Windows this returns a list of QFileInfo objects containing "C:/", - "D:/", etc. On other operating systems, it returns a list containing + "D:/", etc. This does not return drives with ejectable media that are empty. + On other operating systems, it returns a list containing just one root directory (i.e. "/"). \sa root(), rootPath() @@ -1944,6 +2022,8 @@ QFileInfoList QDir::drives() Returns \c true if the directory was successfully changed; otherwise returns \c false. + \snippet code/src_corelib_io_qdir.cpp 16 + \sa current(), currentPath(), home(), root(), temp() */ bool QDir::setCurrent(const QString &path) @@ -2127,7 +2207,7 @@ QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormaliza { const bool allowUncPaths = flags.testAnyFlag(QDirPrivate::AllowUncPaths); const bool isRemote = flags.testAnyFlag(QDirPrivate::RemotePath); - const int len = name.length(); + const qsizetype len = name.size(); if (ok) *ok = false; @@ -2135,15 +2215,15 @@ QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormaliza if (len == 0) return name; - int i = len - 1; + qsizetype i = len - 1; QVarLengthArray<char16_t> outVector(len); - int used = len; + qsizetype used = len; char16_t *out = outVector.data(); - const ushort *p = name.utf16(); - const ushort *prefix = p; - int up = 0; + const char16_t *p = reinterpret_cast<const char16_t *>(name.data()); + const char16_t *prefix = p; + qsizetype up = 0; - const int prefixLength = rootLength(name, allowUncPaths); + const qsizetype prefixLength = rootLength(name, allowUncPaths); p += prefixLength; i -= prefixLength; @@ -2154,10 +2234,10 @@ QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormaliza --i; } - auto isDot = [](const ushort *p, int i) { + auto isDot = [](const char16_t *p, qsizetype i) { return i > 1 && p[i - 1] == '.' && p[i - 2] == '/'; }; - auto isDotDot = [](const ushort *p, int i) { + auto isDotDot = [](const char16_t *p, qsizetype i) { return i > 2 && p[i - 1] == '.' && p[i - 2] == '.' && p[i - 3] == '/'; }; @@ -2256,11 +2336,11 @@ QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormaliza if (prefixLength) { if (!isEmpty && out[used] == '/') { - // Eventhough there is a prefix the out string is a slash. This happens, if the input + // Even though there is a prefix the out string is a slash. This happens, if the input // string only consists of a prefix followed by one or more slashes. Just skip the slash. ++used; } - for (int i = prefixLength - 1; i >= 0; --i) + for (qsizetype i = prefixLength - 1; i >= 0; --i) out[--used] = prefix[i]; } else { if (isEmpty) { @@ -2278,21 +2358,23 @@ QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormaliza // If path was not modified return the original value if (used == 0) return name; - return QString::fromUtf16(out + used, len - used); + return QStringView(out + used, len - used).toString(); } static QString qt_cleanPath(const QString &path, bool *ok) { - if (path.isEmpty()) + if (path.isEmpty()) { + Q_ASSERT(!ok); // The only caller passing ok knows its path is non-empty return path; + } QString name = QDir::fromNativeSeparators(path); QString ret = qt_normalizePathSegments(name, OSSupportsUncPaths ? QDirPrivate::AllowUncPaths : QDirPrivate::DefaultNormalization, ok); // Strip away last slash except for root directories - if (ret.length() > 1 && ret.endsWith(QLatin1Char('/'))) { + if (ret.size() > 1 && ret.endsWith(u'/')) { #if defined (Q_OS_WIN) - if (!(ret.length() == 3 && ret.at(1) == QLatin1Char(':'))) + if (!(ret.length() == 3 && ret.at(1) == u':')) #endif ret.chop(1); } @@ -2336,10 +2418,8 @@ bool QDir::isRelativePath(const QString &path) */ void QDir::refresh() const { - QDirPrivate *d = const_cast<QDir*>(this)->d_ptr.data(); - d->metaData.clear(); - d->initFileEngine(); - d->clearFileLists(); + QDirPrivate *d = const_cast<QDir *>(this)->d_func(); + d->clearCache(QDirPrivate::IncludingMetaData); } /*! @@ -2362,59 +2442,6 @@ QStringList QDir::nameFiltersFromString(const QString &nameFilter) return QDirPrivate::splitFilters(nameFilter); } -/*! - \macro void Q_INIT_RESOURCE(name) - \relates QDir - - Initializes the resources specified by the \c .qrc file with the - specified base \a name. Normally, when resources are built as part - of the application, the resources are loaded automatically at - startup. The Q_INIT_RESOURCE() macro is necessary on some platforms - for resources stored in a static library. - - For example, if your application's resources are listed in a file - called \c myapp.qrc, you can ensure that the resources are - initialized at startup by adding this line to your \c main() - function: - - \snippet code/src_corelib_io_qdir.cpp 13 - - If the file name contains characters that cannot be part of a valid C++ function name - (such as '-'), they have to be replaced by the underscore character ('_'). - - \note This macro cannot be used in a namespace. It should be called from - main(). If that is not possible, the following workaround can be used - to init the resource \c myapp from the function \c{MyNamespace::myFunction}: - - \snippet code/src_corelib_io_qdir.cpp 14 - - \sa Q_CLEANUP_RESOURCE(), {The Qt Resource System} -*/ - -/*! - \since 4.1 - \macro void Q_CLEANUP_RESOURCE(name) - \relates QDir - - Unloads the resources specified by the \c .qrc file with the base - name \a name. - - Normally, Qt resources are unloaded automatically when the - application terminates, but if the resources are located in a - plugin that is being unloaded, call Q_CLEANUP_RESOURCE() to force - removal of your resources. - - \note This macro cannot be used in a namespace. Please see the - Q_INIT_RESOURCE documentation for a workaround. - - Example: - - \snippet code/src_corelib_io_qdir.cpp 15 - - \sa Q_INIT_RESOURCE(), {The Qt Resource System} -*/ - - #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, QDir::Filters filters) { @@ -2422,25 +2449,25 @@ QDebug operator<<(QDebug debug, QDir::Filters filters) debug.resetFormat(); QStringList flags; if (filters == QDir::NoFilter) { - flags << QLatin1String("NoFilter"); + flags << "NoFilter"_L1; } else { - if (filters & QDir::Dirs) flags << QLatin1String("Dirs"); - if (filters & QDir::AllDirs) flags << QLatin1String("AllDirs"); - if (filters & QDir::Files) flags << QLatin1String("Files"); - if (filters & QDir::Drives) flags << QLatin1String("Drives"); - if (filters & QDir::NoSymLinks) flags << QLatin1String("NoSymLinks"); - if (filters & QDir::NoDot) flags << QLatin1String("NoDot"); - if (filters & QDir::NoDotDot) flags << QLatin1String("NoDotDot"); - if ((filters & QDir::AllEntries) == QDir::AllEntries) flags << QLatin1String("AllEntries"); - if (filters & QDir::Readable) flags << QLatin1String("Readable"); - if (filters & QDir::Writable) flags << QLatin1String("Writable"); - if (filters & QDir::Executable) flags << QLatin1String("Executable"); - if (filters & QDir::Modified) flags << QLatin1String("Modified"); - if (filters & QDir::Hidden) flags << QLatin1String("Hidden"); - if (filters & QDir::System) flags << QLatin1String("System"); - if (filters & QDir::CaseSensitive) flags << QLatin1String("CaseSensitive"); + if (filters & QDir::Dirs) flags << "Dirs"_L1; + if (filters & QDir::AllDirs) flags << "AllDirs"_L1; + if (filters & QDir::Files) flags << "Files"_L1; + if (filters & QDir::Drives) flags << "Drives"_L1; + if (filters & QDir::NoSymLinks) flags << "NoSymLinks"_L1; + if (filters & QDir::NoDot) flags << "NoDot"_L1; + if (filters & QDir::NoDotDot) flags << "NoDotDot"_L1; + if ((filters & QDir::AllEntries) == QDir::AllEntries) flags << "AllEntries"_L1; + if (filters & QDir::Readable) flags << "Readable"_L1; + if (filters & QDir::Writable) flags << "Writable"_L1; + if (filters & QDir::Executable) flags << "Executable"_L1; + if (filters & QDir::Modified) flags << "Modified"_L1; + if (filters & QDir::Hidden) flags << "Hidden"_L1; + if (filters & QDir::System) flags << "System"_L1; + if (filters & QDir::CaseSensitive) flags << "CaseSensitive"_L1; } - debug.noquote() << "QDir::Filters(" << flags.join(QLatin1Char('|')) << ')'; + debug.noquote() << "QDir::Filters(" << flags.join(u'|') << ')'; return debug; } @@ -2452,18 +2479,18 @@ static QDebug operator<<(QDebug debug, QDir::SortFlags sorting) debug << "QDir::SortFlags(NoSort)"; } else { QString type; - if ((sorting & QDir::SortByMask) == QDir::Name) type = QLatin1String("Name"); - if ((sorting & QDir::SortByMask) == QDir::Time) type = QLatin1String("Time"); - if ((sorting & QDir::SortByMask) == QDir::Size) type = QLatin1String("Size"); - if ((sorting & QDir::SortByMask) == QDir::Unsorted) type = QLatin1String("Unsorted"); + if ((sorting & QDir::SortByMask) == QDir::Name) type = "Name"_L1; + if ((sorting & QDir::SortByMask) == QDir::Time) type = "Time"_L1; + if ((sorting & QDir::SortByMask) == QDir::Size) type = "Size"_L1; + if ((sorting & QDir::SortByMask) == QDir::Unsorted) type = "Unsorted"_L1; QStringList flags; - if (sorting & QDir::DirsFirst) flags << QLatin1String("DirsFirst"); - if (sorting & QDir::DirsLast) flags << QLatin1String("DirsLast"); - if (sorting & QDir::IgnoreCase) flags << QLatin1String("IgnoreCase"); - if (sorting & QDir::LocaleAware) flags << QLatin1String("LocaleAware"); - if (sorting & QDir::Type) flags << QLatin1String("Type"); - debug.noquote() << "QDir::SortFlags(" << type << '|' << flags.join(QLatin1Char('|')) << ')'; + if (sorting & QDir::DirsFirst) flags << "DirsFirst"_L1; + if (sorting & QDir::DirsLast) flags << "DirsLast"_L1; + if (sorting & QDir::IgnoreCase) flags << "IgnoreCase"_L1; + if (sorting & QDir::LocaleAware) flags << "LocaleAware"_L1; + if (sorting & QDir::Type) flags << "Type"_L1; + debug.noquote() << "QDir::SortFlags(" << type << '|' << flags.join(u'|') << ')'; } return debug; } @@ -2473,7 +2500,7 @@ QDebug operator<<(QDebug debug, const QDir &dir) QDebugStateSaver save(debug); debug.resetFormat(); debug << "QDir(" << dir.path() << ", nameFilters = {" - << dir.nameFilters().join(QLatin1Char(',')) + << dir.nameFilters().join(u',') << "}, " << dir.sorting() << ',' |