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 "qdir.h"
6#include "qdir_p.h"
7#include "qabstractfileengine_p.h"
8#include "qfsfileengine_p.h"
9#ifndef QT_NO_DEBUG_STREAM
10#include "qdebug.h"
11#endif
12#include "qdirlisting.h"
13#include "qdatetime.h"
14#include "qstring.h"
15#if QT_CONFIG(regularexpression)
16# include <qregularexpression.h>
17#endif
18#include "qvarlengtharray.h"
19#include "qfilesystementry_p.h"
20#include "qfilesystemmetadata_p.h"
21#include "qfilesystemengine_p.h"
22#include <qstringbuilder.h>
23
24#ifndef QT_BOOTSTRAPPED
25# include <qcollator.h>
26# include "qreadwritelock.h"
27# include "qmutex.h"
28#endif
29
30#include <private/qorderedmutexlocker_p.h>
31
32#include <algorithm>
33#include <memory>
34#include <stdlib.h>
35
36QT_BEGIN_NAMESPACE
37
38using namespace Qt::StringLiterals;
39
40#if defined(Q_OS_WIN)
41static QString driveSpec(const QString &path)
42{
43 if (path.size() < 2)
44 return QString();
45 char c = path.at(0).toLatin1();
46 if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z'))
47 return QString();
48 if (path.at(1).toLatin1() != ':')
49 return QString();
50 return path.mid(0, 2);
51}
52#endif
53
54enum {
55#if defined(Q_OS_WIN)
56 OSSupportsUncPaths = true
57#else
58 OSSupportsUncPaths = false
59#endif
60};
61
62// Return the length of the root part of an absolute path, for use by cleanPath(), cd().
63static qsizetype rootLength(QStringView name, bool allowUncPaths)
64{
65 const qsizetype len = name.size();
66 // starts with double slash
67 if (allowUncPaths && name.startsWith(s: "//"_L1)) {
68 // Server name '//server/path' is part of the prefix.
69 const qsizetype nextSlash = name.indexOf(c: u'/', from: 2);
70 return nextSlash >= 0 ? nextSlash + 1 : len;
71 }
72#if defined(Q_OS_WIN)
73 if (len >= 2 && name.at(1) == u':') {
74 // Handle a possible drive letter
75 return len > 2 && name.at(2) == u'/' ? 3 : 2;
76 }
77#endif
78 if (len && name.at(n: 0) == u'/')
79 return 1;
80 return 0;
81}
82
83//************* QDirPrivate
84QDirPrivate::QDirPrivate(const QString &path, const QStringList &nameFilters_,
85 QDir::SortFlags sort_, QDir::Filters filters_)
86 : QSharedData(), nameFilters(nameFilters_), sort(sort_), filters(filters_)
87{
88 setPath(path.isEmpty() ? QString::fromLatin1(ba: ".") : path);
89
90 auto isEmpty = [](const auto &e) { return e.isEmpty(); };
91 const bool empty = std::all_of(first: nameFilters.cbegin(), last: nameFilters.cend(), pred: isEmpty);
92 if (empty)
93 nameFilters = QStringList(QString::fromLatin1(ba: "*"));
94}
95
96QDirPrivate::QDirPrivate(const QDirPrivate &copy)
97 : QSharedData(copy),
98 // mutex is not copied
99 nameFilters(copy.nameFilters),
100 sort(copy.sort),
101 filters(copy.filters),
102 // fileEngine is not copied
103 dirEntry(copy.dirEntry)
104{
105 QMutexLocker locker(&copy.fileCache.mutex);
106 fileCache.fileListsInitialized = copy.fileCache.fileListsInitialized.load();
107 fileCache.files = copy.fileCache.files;
108 fileCache.fileInfos = copy.fileCache.fileInfos;
109 fileCache.absoluteDirEntry = copy.fileCache.absoluteDirEntry;
110 fileCache.metaData = copy.fileCache.metaData;
111}
112
113bool QDirPrivate::exists() const
114{
115 if (!fileEngine) {
116 QMutexLocker locker(&fileCache.mutex);
117 QFileSystemEngine::fillMetaData(
118 entry: dirEntry, data&: fileCache.metaData,
119 what: QFileSystemMetaData::ExistsAttribute
120 | QFileSystemMetaData::DirectoryType); // always stat
121 return fileCache.metaData.exists() && fileCache.metaData.isDirectory();
122 }
123 const QAbstractFileEngine::FileFlags info =
124 fileEngine->fileFlags(type: QAbstractFileEngine::DirectoryType
125 | QAbstractFileEngine::ExistsFlag
126 | QAbstractFileEngine::Refresh);
127 if (!(info & QAbstractFileEngine::DirectoryType))
128 return false;
129 return info.testAnyFlag(flag: QAbstractFileEngine::ExistsFlag);
130}
131
132// static
133inline QChar QDirPrivate::getFilterSepChar(const QString &nameFilter)
134{
135 QChar sep(u';');
136 qsizetype i = nameFilter.indexOf(c: sep, from: 0);
137 if (i == -1 && nameFilter.indexOf(c: u' ', from: 0) != -1)
138 sep = QChar(u' ');
139 return sep;
140}
141
142// static
143inline QStringList QDirPrivate::splitFilters(const QString &nameFilter, QChar sep)
144{
145 if (sep.isNull())
146 sep = getFilterSepChar(nameFilter);
147 QStringList ret;
148 for (auto e : qTokenize(h: nameFilter, n&: sep))
149 ret.append(t: e.trimmed().toString());
150 return ret;
151}
152
153inline void QDirPrivate::setPath(const QString &path)
154{
155 QString p = QDir::fromNativeSeparators(pathName: path);
156 if (p.endsWith(c: u'/')
157 && p.size() > 1
158#if defined(Q_OS_WIN)
159 && (!(p.length() == 3 && p.at(1).unicode() == ':' && p.at(0).isLetter()))
160#endif
161 ) {
162 p.truncate(pos: p.size() - 1);
163 }
164 dirEntry = QFileSystemEntry(p, QFileSystemEntry::FromInternalPath());
165 clearCache(mode: IncludingMetaData);
166 fileCache.absoluteDirEntry = QFileSystemEntry();
167}
168
169inline QString QDirPrivate::resolveAbsoluteEntry() const
170{
171 QMutexLocker locker(&fileCache.mutex);
172 if (!fileCache.absoluteDirEntry.isEmpty())
173 return fileCache.absoluteDirEntry.filePath();
174
175 if (dirEntry.isEmpty())
176 return dirEntry.filePath();
177
178 QString absoluteName;
179 if (!fileEngine) {
180 if (!dirEntry.isRelative() && dirEntry.isClean()) {
181 fileCache.absoluteDirEntry = dirEntry;
182 return dirEntry.filePath();
183 }
184
185 absoluteName = QFileSystemEngine::absoluteName(entry: dirEntry).filePath();
186 } else {
187 absoluteName = fileEngine->fileName(file: QAbstractFileEngine::AbsoluteName);
188 }
189 auto absoluteFileSystemEntry =
190 QFileSystemEntry(QDir::cleanPath(path: absoluteName), QFileSystemEntry::FromInternalPath());
191 fileCache.absoluteDirEntry = absoluteFileSystemEntry;
192 return absoluteFileSystemEntry.filePath();
193}
194
195/* For sorting */
196struct QDirSortItem
197{
198 QDirSortItem() = default;
199 QDirSortItem(const QFileInfo &fi, QDir::SortFlags sort)
200 : item(fi)
201 {
202 // A dir e.g. "dirA.bar" doesn't have actually have an extension/suffix, when
203 // sorting by type such "suffix" should be ignored but that would complicate
204 // the code and uses can change the behavior by setting DirsFirst/DirsLast
205 if (sort.testAnyFlag(flag: QDir::Type))
206 suffix_cache = item.suffix();
207 }
208
209 mutable QString filename_cache;
210 QString suffix_cache;
211 QFileInfo item;
212};
213
214class QDirSortItemComparator
215{
216 QDir::SortFlags qt_cmp_si_sort_flags;
217
218#ifndef QT_BOOTSTRAPPED
219 QCollator *collator = nullptr;
220#endif
221public:
222#ifndef QT_BOOTSTRAPPED
223 QDirSortItemComparator(QDir::SortFlags flags, QCollator *coll = nullptr)
224 : qt_cmp_si_sort_flags(flags), collator(coll)
225 {
226 Q_ASSERT(!qt_cmp_si_sort_flags.testAnyFlag(QDir::LocaleAware) || collator);
227
228 if (collator && qt_cmp_si_sort_flags.testAnyFlag(flag: QDir::IgnoreCase))
229 collator->setCaseSensitivity(Qt::CaseInsensitive);
230 }
231#else
232 QDirSortItemComparator(QDir::SortFlags flags)
233 : qt_cmp_si_sort_flags(flags)
234 {
235 }
236#endif
237 bool operator()(const QDirSortItem &, const QDirSortItem &) const;
238
239 int compareStrings(const QString &a, const QString &b, Qt::CaseSensitivity cs) const
240 {
241#ifndef QT_BOOTSTRAPPED
242 if (collator)
243 return collator->compare(s1: a, s2: b);
244#endif
245 return a.compare(s: b, cs);
246 }
247};
248
249bool QDirSortItemComparator::operator()(const QDirSortItem &n1, const QDirSortItem &n2) const
250{
251 const QDirSortItem* f1 = &n1;
252 const QDirSortItem* f2 = &n2;
253
254 if ((qt_cmp_si_sort_flags & QDir::DirsFirst) && (f1->item.isDir() != f2->item.isDir()))
255 return f1->item.isDir();
256 if ((qt_cmp_si_sort_flags & QDir::DirsLast) && (f1->item.isDir() != f2->item.isDir()))
257 return !f1->item.isDir();
258
259 const bool ic = qt_cmp_si_sort_flags.testAnyFlag(flag: QDir::IgnoreCase);
260 const auto qtcase = ic ? Qt::CaseInsensitive : Qt::CaseSensitive;
261
262 qint64 r = 0;
263 int sortBy = ((qt_cmp_si_sort_flags & QDir::SortByMask)
264 | (qt_cmp_si_sort_flags & QDir::Type)).toInt();
265
266 switch (sortBy) {
267 case QDir::Time: {
268 const QDateTime firstModified = f1->item.lastModified(tz: QTimeZone::UTC);
269 const QDateTime secondModified = f2->item.lastModified(tz: QTimeZone::UTC);
270 r = firstModified.msecsTo(secondModified);
271 break;
272 }
273 case QDir::Size:
274 r = f2->item.size() - f1->item.size();
275 break;
276 case QDir::Type:
277 r = compareStrings(a: f1->suffix_cache, b: f2->suffix_cache, cs: qtcase);
278 break;
279 default:
280 ;
281 }
282
283 if (r == 0 && sortBy != QDir::Unsorted) {
284 // Still not sorted - sort by name
285
286 if (f1->filename_cache.isNull())
287 f1->filename_cache = f1->item.fileName();
288 if (f2->filename_cache.isNull())
289 f2->filename_cache = f2->item.fileName();
290
291 r = compareStrings(a: f1->filename_cache, b: f2->filename_cache, cs: qtcase);
292 }
293 if (qt_cmp_si_sort_flags & QDir::Reversed)
294 return r > 0;
295 return r < 0;
296}
297
298inline void QDirPrivate::sortFileList(QDir::SortFlags sort, const QFileInfoList &l,
299 QStringList *names, QFileInfoList *infos)
300{
301 Q_ASSERT(names || infos);
302 Q_ASSERT(!infos || infos->isEmpty());
303 Q_ASSERT(!names || names->isEmpty());
304
305 const qsizetype n = l.size();
306 if (n == 0)
307 return;
308
309 if (n == 1 || (sort & QDir::SortByMask) == QDir::Unsorted) {
310 if (infos)
311 *infos = l;
312
313 if (names) {
314 for (const QFileInfo &fi : l)
315 names->append(t: fi.fileName());
316 }
317 } else {
318 QVarLengthArray<QDirSortItem, 64> si;
319 si.reserve(sz: n);
320 for (qsizetype i = 0; i < n; ++i)
321 si.emplace_back(args: l.at(i), args&: sort);
322
323#ifndef QT_BOOTSTRAPPED
324 if (sort.testAnyFlag(flag: QDir::LocaleAware)) {
325 QCollator coll;
326 std::sort(first: si.data(), last: si.data() + n, comp: QDirSortItemComparator(sort, &coll));
327 } else {
328 std::sort(first: si.data(), last: si.data() + n, comp: QDirSortItemComparator(sort));
329 }
330#else
331 std::sort(si.data(), si.data() + n, QDirSortItemComparator(sort));
332#endif // QT_BOOTSTRAPPED
333
334 // put them back in the list(s)
335 for (qsizetype i = 0; i < n; ++i) {
336 auto &fileInfo = si[i].item;
337 if (infos)
338 infos->append(t: fileInfo);
339 if (names) {
340 const bool cached = !si[i].filename_cache.isNull();
341 names->append(t: cached ? si[i].filename_cache : fileInfo.fileName());
342 }
343 }
344 }
345}
346
347inline void QDirPrivate::initFileLists(const QDir &dir) const
348{
349 QMutexLocker locker(&fileCache.mutex);
350 if (!fileCache.fileListsInitialized) {
351 QFileInfoList l;
352 for (const auto &dirEntry : QDirListing(dir.path(), dir.nameFilters(),
353 dir.filter().toInt())) {
354 l.emplace_back(args: dirEntry.fileInfo());
355 }
356
357 sortFileList(sort, l, names: &fileCache.files, infos: &fileCache.fileInfos);
358 fileCache.fileListsInitialized = true;
359 }
360}
361
362inline void QDirPrivate::clearCache(MetaDataClearing mode)
363{
364 QMutexLocker locker(&fileCache.mutex);
365 if (mode == IncludingMetaData)
366 fileCache.metaData.clear();
367 fileCache.fileListsInitialized = false;
368 fileCache.files.clear();
369 fileCache.fileInfos.clear();
370 fileEngine = QFileSystemEngine::createLegacyEngine(entry&: dirEntry, data&: fileCache.metaData);
371}
372
373/*!
374 \class QDir
375 \inmodule QtCore
376 \brief The QDir class provides access to directory structures and their contents.
377
378 \ingroup io
379 \ingroup shared
380 \reentrant
381
382 \compares equality
383
384 A QDir is used to manipulate path names, access information
385 regarding paths and files, and manipulate the underlying file
386 system. It can also be used to access Qt's \l{resource system}.
387
388 Qt uses "/" as a universal directory separator in the same way
389 that "/" is used as a path separator in URLs. If you always use
390 "/" as a directory separator, Qt will translate your paths to
391 conform to the underlying operating system.
392
393 A QDir can point to a file using either a relative or an absolute
394 path. Absolute paths begin with the directory separator
395 (optionally preceded by a drive specification under Windows).
396 Relative file names begin with a directory name or a file name and
397 specify a path relative to the current directory.
398
399 Examples of absolute paths:
400
401 \snippet code/src_corelib_io_qdir.cpp 0
402
403 On Windows, the second example above will be translated to
404 \c{C:\Users} when used to access files.
405
406 Examples of relative paths:
407
408 \snippet code/src_corelib_io_qdir.cpp 1
409
410 You can use the isRelative() or isAbsolute() functions to check if
411 a QDir is using a relative or an absolute file path. Call
412 makeAbsolute() to convert a relative QDir to an absolute one.
413
414 \note Paths starting with a colon (\e{:}) are always considered
415 absolute, as they denote a QResource.
416
417 \section1 Navigation and Directory Operations
418
419 A directory's path can be obtained with the path() function, and
420 a new path set with the setPath() function. The absolute path to
421 a directory is found by calling absolutePath().
422
423 The name of a directory is found using the dirName() function. This
424 typically returns the last element in the absolute path that specifies
425 the location of the directory. However, it can also return "." if
426 the QDir represents the current directory.
427
428 \snippet code/src_corelib_io_qdir.cpp 2
429
430 The path for a directory can also be changed with the cd() and cdUp()
431 functions, both of which operate like familiar shell commands.
432 When cd() is called with the name of an existing directory, the QDir
433 object changes directory so that it represents that directory instead.
434 The cdUp() function changes the directory of the QDir object so that
435 it refers to its parent directory; i.e. cd("..") is equivalent to
436 cdUp().
437
438 Directories can be created with mkdir(), renamed with rename(), and
439 removed with rmdir().
440
441 You can test for the presence of a directory with a given name by
442 using exists(), and the properties of a directory can be tested with
443 isReadable(), isAbsolute(), isRelative(), and isRoot().
444
445 The refresh() function re-reads the directory's data from disk.
446
447 \section1 Files and Directory Contents
448
449 Directories contain a number of entries, representing files,
450 directories, and symbolic links. The number of entries in a
451 directory is returned by count().
452 A string list of the names of all the entries in a directory can be
453 obtained with entryList(). If you need information about each
454 entry, use entryInfoList() to obtain a list of QFileInfo objects.
455
456 Paths to files and directories within a directory can be
457 constructed using filePath() and absoluteFilePath().
458 The filePath() function returns a path to the specified file
459 or directory relative to the path of the QDir object;
460 absoluteFilePath() returns an absolute path to the specified
461 file or directory. Neither of these functions checks for the
462 existence of files or directory; they only construct paths.
463
464 \snippet code/src_corelib_io_qdir.cpp 3
465
466 Files can be removed by using the remove() function. Directories
467 cannot be removed in the same way as files; use rmdir() to remove
468 them instead.
469
470 It is possible to reduce the number of entries returned by
471 entryList() and entryInfoList() by applying filters to a QDir object.
472 You can apply a name filter to specify a pattern with wildcards that
473 file names need to match, an attribute filter that selects properties
474 of entries and can distinguish between files and directories, and a
475 sort order.
476
477 Name filters are lists of strings that are passed to setNameFilters().
478 Attribute filters consist of a bitwise OR combination of Filters, and
479 these are specified when calling setFilter().
480 The sort order is specified using setSorting() with a bitwise OR
481 combination of SortFlags.
482
483 You can test to see if a filename matches a filter using the match()
484 function.
485
486 Filter and sort order flags may also be specified when calling
487 entryList() and entryInfoList() in order to override previously defined
488 behavior.
489
490 \section1 The Current Directory and Other Special Paths
491
492 Access to some common directories is provided with a number of static
493 functions that return QDir objects. There are also corresponding functions
494 for these that return strings:
495
496 \table
497 \header \li QDir \li QString \li Return Value
498 \row \li current() \li currentPath() \li The application's working directory
499 \row \li home() \li homePath() \li The user's home directory
500 \row \li root() \li rootPath() \li The root directory
501 \row \li temp() \li tempPath() \li The system's temporary directory
502 \endtable
503
504 The setCurrent() static function can also be used to set the application's
505 working directory.
506
507 If you want to find the directory containing the application's executable,
508 see \l{QCoreApplication::applicationDirPath()}.
509
510 The drives() static function provides a list of root directories for each
511 device that contains a filing system. On Unix systems this returns a list
512 containing a single root directory "/"; on Windows the list will usually
513 contain \c{C:/}, and possibly other drive letters such as \c{D:/}, depending
514 on the configuration of the user's system.
515
516 \section1 Path Manipulation and Strings
517
518 Paths containing "." elements that reference the current directory at that
519 point in the path, ".." elements that reference the parent directory, and
520 symbolic links can be reduced to a canonical form using the canonicalPath()
521 function.
522
523 Paths can also be simplified by using cleanPath() to remove redundant "/"
524 and ".." elements.
525
526 It is sometimes necessary to be able to show a path in the native
527 representation for the user's platform. The static toNativeSeparators()
528 function returns a copy of the specified path in which each directory
529 separator is replaced by the appropriate separator for the underlying
530 operating system.
531
532 \section1 Examples
533
534 Check if a directory exists:
535
536 \snippet code/src_corelib_io_qdir.cpp 4
537
538 (We could also use one of the static convenience functions
539 QFileInfo::exists() or QFile::exists().)
540
541 Traversing directories and reading a file:
542
543 \snippet code/src_corelib_io_qdir.cpp 5
544
545 A program that lists all the files in the current directory
546 (excluding symbolic links), sorted by size, smallest first:
547
548 \snippet qdir-listfiles/main.cpp 0
549
550 \section1 Platform Specific Issues
551
552 \include android-content-uri-limitations.qdocinc
553
554 \sa QFileInfo, QFile, QFileDialog, QCoreApplication::applicationDirPath(),
555 {Fetch More Example}
556*/
557
558/*!
559 \fn QDir &QDir::operator=(QDir &&other)
560
561 Move-assigns \a other to this QDir instance.
562
563 \since 5.2
564*/
565
566/*!
567 \internal
568*/
569QDir::QDir(QDirPrivate &p) : d_ptr(&p)
570{
571}
572
573/*!
574 Constructs a QDir pointing to the given directory \a path. If path
575 is empty the program's working directory, ("."), is used.
576
577 \sa currentPath()
578*/
579QDir::QDir(const QString &path) : d_ptr(new QDirPrivate(path))
580{
581}
582
583/*!
584 Constructs a QDir with path \a path, that filters its entries by
585 name using \a nameFilter and by attributes using \a filters. It
586 also sorts the names using \a sort.
587
588 The default \a nameFilter is an empty string, which excludes
589 nothing; the default \a filters is \l AllEntries, which also
590 excludes nothing. The default \a sort is \l Name | \l IgnoreCase,
591 i.e. sort by name case-insensitively.
592
593 If \a path is an empty string, QDir uses "." (the current
594 directory). If \a nameFilter is an empty string, QDir uses the
595 name filter "*" (all files).
596
597 \note \a path need not exist.
598
599 \sa exists(), setPath(), setNameFilters(), setFilter(), setSorting()
600*/
601QDir::QDir(const QString &path, const QString &nameFilter,
602 SortFlags sort, Filters filters)
603 : d_ptr(new QDirPrivate(path, QDir::nameFiltersFromString(nameFilter), sort, filters))
604{
605}
606
607/*!
608 Constructs a QDir object that is a copy of the QDir object for
609 directory \a dir.
610
611 \sa operator=()
612*/
613QDir::QDir(const QDir &dir)
614 : d_ptr(dir.d_ptr)
615{
616}
617
618/*!
619 Destroys the QDir object frees up its resources. This has no
620 effect on the underlying directory in the file system.
621*/
622QDir::~QDir()
623{
624}
625
626/*!
627 Sets the path of the directory to \a path. The path is cleaned of
628 redundant ".", ".." and of multiple separators. No check is made
629 to see whether a directory with this path actually exists; but you
630 can check for yourself using exists().
631
632 The path can be either absolute or relative. Absolute paths begin
633 with the directory separator "/" (optionally preceded by a drive
634 specification under Windows). Relative file names begin with a
635 directory name or a file name and specify a path relative to the
636 current directory. An example of an absolute path is the string
637 "/tmp/quartz", a relative path might look like "src/fatlib".
638
639 \sa path(), absolutePath(), exists(), cleanPath(), dirName(),
640 absoluteFilePath(), isRelative(), makeAbsolute()
641*/
642void QDir::setPath(const QString &path)
643{
644 d_ptr->setPath(path);
645}
646
647/*!
648 Returns the path. This may contain symbolic links, but never
649 contains redundant ".", ".." or multiple separators.
650
651 The returned path can be either absolute or relative (see
652 setPath()).
653
654 \sa setPath(), absolutePath(), exists(), cleanPath(), dirName(),
655 absoluteFilePath(), toNativeSeparators(), makeAbsolute()
656*/
657QString QDir::path() const
658{
659 Q_D(const QDir);
660 return d->dirEntry.filePath();
661}
662
663/*!
664 Returns the absolute path (a path that starts with "/" or with a
665 drive specification), which may contain symbolic links, but never
666 contains redundant ".", ".." or multiple separators.
667
668 \sa setPath(), canonicalPath(), exists(), cleanPath(),
669 dirName(), absoluteFilePath()
670*/
671QString QDir::absolutePath() const
672{
673 Q_D(const QDir);
674 if (!d->fileEngine)
675 return d->resolveAbsoluteEntry();
676
677 return d->fileEngine->fileName(file: QAbstractFileEngine::AbsoluteName);
678}
679
680/*!
681 Returns the canonical path, i.e. a path without symbolic links or
682 redundant "." or ".." elements.
683
684 On systems that do not have symbolic links this function will
685 always return the same string that absolutePath() returns. If the
686 canonical path does not exist (normally due to dangling symbolic
687 links) canonicalPath() returns an empty string.
688
689 Example:
690
691 \snippet code/src_corelib_io_qdir.cpp 6
692
693 \sa path(), absolutePath(), exists(), cleanPath(), dirName(),
694 absoluteFilePath()
695*/
696QString QDir::canonicalPath() const
697{
698 Q_D(const QDir);
699 if (!d->fileEngine) {
700 QMutexLocker locker(&d->fileCache.mutex);
701 QFileSystemEntry answer =
702 QFileSystemEngine::canonicalName(entry: d->dirEntry, data&: d->fileCache.metaData);
703 return answer.filePath();
704 }
705 return d->fileEngine->fileName(file: QAbstractFileEngine::CanonicalName);
706}
707
708/*!
709 Returns the name of the directory; this is \e not the same as the
710 path, e.g. a directory with the name "mail", might have the path
711 "/var/spool/mail". If the directory has no name (e.g. it is the
712 root directory) an empty string is returned.
713
714 No check is made to ensure that a directory with this name
715 actually exists; but see exists().
716
717 \sa path(), filePath(), absolutePath(), absoluteFilePath()
718*/
719QString QDir::dirName() const
720{
721 Q_D(const QDir);
722 if (!d_ptr->fileEngine)
723 return d->dirEntry.fileName();
724 return d->fileEngine->fileName(file: QAbstractFileEngine::BaseName);
725}
726
727
728#ifdef Q_OS_WIN
729static qsizetype drivePrefixLength(QStringView path)
730{
731 // Used to extract path's drive for use as prefix for an "absolute except for drive" path
732 const qsizetype size = path.size();
733 qsizetype drive = 2; // length of drive prefix
734 if (size > 1 && path.at(1).unicode() == ':') {
735 if (Q_UNLIKELY(!path.at(0).isLetter()))
736 return 0;
737 } else if (path.startsWith("//"_L1)) {
738 // UNC path; use its //server/share part as "drive" - it's as sane a
739 // thing as we can do.
740 for (int i = 0 ; i < 2 ; ++i) { // Scan two "path fragments":
741 while (drive < size && path.at(drive).unicode() == '/')
742 drive++;
743 if (drive >= size) {
744 qWarning("Base directory starts with neither a drive nor a UNC share: %s",
745 qUtf8Printable(QDir::toNativeSeparators(path.toString())));
746 return 0;
747 }
748 while (drive < size && path.at(drive).unicode() != '/')
749 drive++;
750 }
751 } else {
752 return 0;
753 }
754 return drive;
755}
756#endif // Q_OS_WIN
757
758static bool treatAsAbsolute(const QString &path)
759{
760 // ### Qt 6: be consistent about absolute paths
761
762 // QFileInfo will use the right FS-engine for virtual file-systems
763 // (e.g. resource paths). Unfortunately, for real file-systems, it relies
764 // on QFileSystemEntry's isRelative(), which is flawed on MS-Win, ignoring
765 // its (correct) isAbsolute(). So only use that isAbsolute() unless there's
766 // a colon in the path.
767 // FIXME: relies on virtual file-systems having colons in their prefixes.
768 // The case of an MS-absolute C:/... path happens to work either way.
769 return (path.contains(c: u':') && QFileInfo(path).isAbsolute())
770 || QFileSystemEntry(path).isAbsolute();
771}
772
773/*!
774 Returns the path name of a file in the directory. Does \e not
775 check if the file actually exists in the directory; but see
776 exists(). If the QDir is relative the returned path name will also
777 be relative. Redundant multiple separators or "." and ".."
778 directories in \a fileName are not removed (see cleanPath()).
779
780 \sa dirName(), absoluteFilePath(), isRelative(), canonicalPath()
781*/
782QString QDir::filePath(const QString &fileName) const
783{
784 if (treatAsAbsolute(path: fileName))
785 return fileName;
786
787 Q_D(const QDir);
788 QString ret = d->dirEntry.filePath();
789 if (fileName.isEmpty())
790 return ret;
791
792#ifdef Q_OS_WIN
793 if (fileName.startsWith(u'/') || fileName.startsWith(u'\\')) {
794 // Handle the "absolute except for drive" case (i.e. \blah not c:\blah):
795 const qsizetype drive = drivePrefixLength(ret);
796 return drive > 0 ? QStringView{ret}.left(drive) % fileName : fileName;
797 }
798#endif // Q_OS_WIN
799
800 if (ret.isEmpty() || ret.endsWith(c: u'/'))
801 return ret % fileName;
802 return ret % u'/' % fileName;
803}
804
805/*!
806 Returns the absolute path name of a file in the directory. Does \e
807 not check if the file actually exists in the directory; but see
808 exists(). Redundant multiple separators or "." and ".."
809 directories in \a fileName are not removed (see cleanPath()).
810
811 \sa relativeFilePath(), filePath(), canonicalPath()
812*/
813QString QDir::absoluteFilePath(const QString &fileName) const
814{
815 if (treatAsAbsolute(path: fileName))
816 return fileName;
817
818 Q_D(const QDir);
819 QString absoluteDirPath = d->resolveAbsoluteEntry();
820 if (fileName.isEmpty())
821 return absoluteDirPath;
822#ifdef Q_OS_WIN
823 // Handle the "absolute except for drive" case (i.e. \blah not c:\blah):
824 if (fileName.startsWith(u'/') || fileName.startsWith(u'\\')) {
825 // Combine absoluteDirPath's drive with fileName
826 const qsizetype drive = drivePrefixLength(absoluteDirPath);
827 if (Q_LIKELY(drive))
828 return QStringView{absoluteDirPath}.left(drive) % fileName;
829
830 qWarning("Base directory's drive is not a letter: %s",
831 qUtf8Printable(QDir::toNativeSeparators(absoluteDirPath)));
832 return QString();
833 }
834#endif // Q_OS_WIN
835 if (!absoluteDirPath.endsWith(c: u'/'))
836 return absoluteDirPath % u'/' % fileName;
837 return absoluteDirPath % fileName;
838}
839
840/*!
841 Returns the path to \a fileName relative to the directory.
842
843 \snippet code/src_corelib_io_qdir.cpp 7
844
845 \sa absoluteFilePath(), filePath(), canonicalPath()
846*/
847QString QDir::relativeFilePath(const QString &fileName) const
848{
849 QString dir = cleanPath(path: absolutePath());
850 QString file = cleanPath(path: fileName);
851
852 if (isRelativePath(path: file) || isRelativePath(path: dir))
853 return file;
854
855#ifdef Q_OS_WIN
856 QString dirDrive = driveSpec(dir);
857 QString fileDrive = driveSpec(file);
858
859 bool fileDriveMissing = false;
860 if (fileDrive.isEmpty()) {
861 fileDrive = dirDrive;
862 fileDriveMissing = true;
863 }
864
865 if (fileDrive.toLower() != dirDrive.toLower()
866 || (file.startsWith("//"_L1)
867 && !dir.startsWith("//"_L1))) {
868 return file;
869 }
870
871 dir.remove(0, dirDrive.size());
872 if (!fileDriveMissing)
873 file.remove(0, fileDrive.size());
874#endif
875
876 QString result;
877 const auto dirElts = dir.tokenize(needle: u'/', flags: Qt::SkipEmptyParts);
878 const auto fileElts = file.tokenize(needle: u'/', flags: Qt::SkipEmptyParts);
879
880 const auto dend = dirElts.end();
881 const auto fend = fileElts.end();
882 auto dit = dirElts.begin();
883 auto fit = fileElts.begin();
884
885 const auto eq = [](QStringView lhs, QStringView rhs) {
886 return
887#if defined(Q_OS_WIN)
888 lhs.compare(rhs, Qt::CaseInsensitive) == 0;
889#else
890 lhs == rhs;
891#endif
892 };
893
894 // std::ranges::mismatch
895 while (dit != dend && fit != fend && eq(*dit, *fit)) {
896 ++dit;
897 ++fit;
898 }
899
900 while (dit != dend) {
901 result += "../"_L1;
902 ++dit;
903 }
904
905 if (fit != fend) {
906 while (fit != fend) {
907 result += *fit++;
908 result += u'/';
909 }
910 result.chop(n: 1);
911 }
912
913 if (result.isEmpty())
914 result = "."_L1;
915 return result;
916}
917
918/*!
919 \since 4.2
920
921 Returns \a pathName with the '/' separators converted to
922 separators that are appropriate for the underlying operating
923 system.
924
925 On Windows, toNativeSeparators("c:/winnt/system32") returns
926 "c:\\winnt\\system32".
927
928 The returned string may be the same as the argument on some
929 operating systems, for example on Unix.
930
931 \sa fromNativeSeparators(), separator()
932*/
933QString QDir::toNativeSeparators(const QString &pathName)
934{
935#if defined(Q_OS_WIN)
936 qsizetype i = pathName.indexOf(u'/');
937 if (i != -1) {
938 QString n(pathName);
939
940 QChar * const data = n.data();
941 data[i++] = u'\\';
942
943 for (; i < n.length(); ++i) {
944 if (data[i] == u'/')
945 data[i] = u'\\';
946 }
947
948 return n;
949 }
950#endif
951 return pathName;
952}
953
954/*!
955 \since 4.2
956
957 Returns \a pathName using '/' as file separator. On Windows,
958 for instance, fromNativeSeparators("\c{c:\\winnt\\system32}") returns
959 "c:/winnt/system32".
960
961 The returned string may be the same as the argument on some
962 operating systems, for example on Unix.
963
964 \sa toNativeSeparators(), separator()
965*/
966QString QDir::fromNativeSeparators(const QString &pathName)
967{
968#if defined(Q_OS_WIN)
969 return QFileSystemEntry::removeUncOrLongPathPrefix(pathName).replace(u'\\', u'/');
970#else
971 return pathName;
972#endif
973}
974
975static bool qt_cleanPath(QString *path);
976
977/*!
978 Changes the QDir's directory to \a dirName.
979
980 Returns \c true if the new directory exists;
981 otherwise returns \c false. Note that the logical cd() operation is
982 not performed if the new directory does not exist.
983
984 Calling cd("..") is equivalent to calling cdUp().
985
986 \sa cdUp(), isReadable(), exists(), path()
987*/
988bool QDir::cd(const QString &dirName)
989{
990 // Don't detach just yet.
991 const QDirPrivate * const d = d_ptr.constData();
992
993 if (dirName.isEmpty() || dirName == u'.')
994 return true;
995 QString newPath;
996 if (isAbsolutePath(path: dirName)) {
997 newPath = dirName;
998 qt_cleanPath(path: &newPath);
999 } else {
1000 newPath = d->dirEntry.filePath();
1001 if (!newPath.endsWith(c: u'/'))
1002 newPath += u'/';
1003 newPath += dirName;
1004 if (dirName.indexOf(c: u'/') >= 0
1005 || dirName == ".."_L1
1006 || d->dirEntry.filePath() == u'.') {
1007 if (!qt_cleanPath(path: &newPath))
1008 return false;
1009 /*
1010 If newPath starts with .., we convert it to absolute to
1011 avoid infinite looping on
1012
1013 QDir dir(".");
1014 while (dir.cdUp())
1015 ;
1016 */
1017 if (newPath.startsWith(s: ".."_L1)) {
1018 newPath = QFileInfo(newPath).absoluteFilePath();
1019 }
1020 }
1021 }
1022
1023 std::unique_ptr<QDirPrivate> dir(new QDirPrivate(*d_ptr.constData()));
1024 dir->setPath(newPath);
1025 if (!dir->exists())
1026 return false;
1027
1028 d_ptr = dir.release();
1029 return true;
1030}
1031
1032/*!
1033 Changes directory by moving one directory up from the QDir's
1034 current directory.
1035
1036 Returns \c true if the new directory exists;
1037 otherwise returns \c false. Note that the logical cdUp() operation is
1038 not performed if the new directory does not exist.
1039
1040 \note On Android, this is not supported for content URIs. For more information,
1041 see \l {Android: DocumentFile.getParentFile()}{DocumentFile.getParentFile()}.
1042
1043 \sa cd(), isReadable(), exists(), path()
1044*/
1045bool QDir::cdUp()
1046{
1047 return cd(dirName: QString::fromLatin1(ba: ".."));
1048}
1049
1050/*!
1051 Returns the string list set by setNameFilters()
1052*/
1053QStringList QDir::nameFilters() const
1054{
1055 Q_D(const QDir);
1056 return d->nameFilters;
1057}
1058
1059/*!
1060 Sets the name filters used by entryList() and entryInfoList() to the
1061 list of filters specified by \a nameFilters.
1062
1063 Each name filter is a wildcard (globbing) filter that understands
1064 \c{*} and \c{?} wildcards. See \l{QRegularExpression::fromWildcard()}.
1065
1066 For example, the following code sets three name filters on a QDir
1067 to ensure that only files with extensions typically used for C++
1068 source files are listed:
1069
1070 \snippet qdir-namefilters/main.cpp 0
1071
1072 \sa nameFilters(), setFilter()
1073*/
1074void QDir::setNameFilters(const QStringList &nameFilters)
1075{
1076 Q_D(QDir);
1077 d->clearCache(mode: QDirPrivate::KeepMetaData);
1078 d->nameFilters = nameFilters;
1079}
1080
1081#ifndef QT_BOOTSTRAPPED
1082
1083namespace {
1084struct DirSearchPaths {
1085 mutable QReadWriteLock mutex;
1086 QHash<QString, QStringList> paths;
1087};
1088}
1089
1090Q_GLOBAL_STATIC(DirSearchPaths, dirSearchPaths)
1091
1092/*!
1093 \since 4.3
1094
1095 Sets or replaces Qt's search paths for file names with the prefix \a prefix
1096 to \a searchPaths.
1097
1098 To specify a prefix for a file name, prepend the prefix followed by a single
1099 colon (e.g., "images:undo.png", "xmldocs:books.xml"). \a prefix can only
1100 contain letters or numbers (e.g., it cannot contain a colon, nor a slash).
1101
1102 Qt uses this search path to locate files with a known prefix. The search
1103 path entries are tested in order, starting with the first entry.
1104
1105 \snippet code/src_corelib_io_qdir.cpp 8
1106
1107 File name prefix must be at least 2 characters long to avoid conflicts with
1108 Windows drive letters.
1109
1110 Search paths may contain paths to \l{The Qt Resource System}.
1111*/
1112void QDir::setSearchPaths(const QString &prefix, const QStringList &searchPaths)
1113{
1114 if (prefix.size() < 2) {
1115 qWarning(msg: "QDir::setSearchPaths: Prefix must be longer than 1 character");
1116 return;
1117 }
1118
1119 for (QChar ch : prefix) {
1120 if (!ch.isLetterOrNumber()) {
1121 qWarning(msg: "QDir::setSearchPaths: Prefix can only contain letters or numbers");
1122 return;
1123 }
1124 }
1125
1126 DirSearchPaths &conf = *dirSearchPaths;
1127 const QWriteLocker lock(&conf.mutex);
1128 if (searchPaths.isEmpty()) {
1129 conf.paths.remove(key: prefix);
1130 } else {
1131 conf.paths.insert(key: prefix, value: searchPaths);
1132 }
1133}
1134
1135/*!
1136 \since 4.3
1137
1138 Adds \a path to the search path for \a prefix.
1139
1140 \sa setSearchPaths()
1141*/
1142void QDir::addSearchPath(const QString &prefix, const QString &path)
1143{
1144 if (path.isEmpty())
1145 return;
1146
1147 DirSearchPaths &conf = *dirSearchPaths;
1148 const QWriteLocker lock(&conf.mutex);
1149 conf.paths[prefix] += path;
1150}
1151
1152/*!
1153 \since 4.3
1154
1155 Returns the search paths for \a prefix.
1156
1157 \sa setSearchPaths(), addSearchPath()
1158*/
1159QStringList QDir::searchPaths(const QString &prefix)
1160{
1161 if (!dirSearchPaths.exists())
1162 return QStringList();
1163
1164 const DirSearchPaths &conf = *dirSearchPaths;
1165 const QReadLocker lock(&conf.mutex);
1166 return conf.paths.value(key: prefix);
1167}
1168
1169#endif // QT_BOOTSTRAPPED
1170
1171/*!
1172 Returns the value set by setFilter()
1173*/
1174QDir::Filters QDir::filter() const
1175{
1176 Q_D(const QDir);
1177 return d->filters;
1178}
1179
1180/*!
1181 \enum QDir::Filter
1182
1183 This enum describes the filtering options available to QDir; e.g.
1184 for entryList() and entryInfoList(). The filter value is specified
1185 by combining values from the following list using the bitwise OR
1186 operator:
1187
1188 \value Dirs List directories that match the filters.
1189 \value AllDirs List all directories; i.e. don't apply the filters
1190 to directory names.
1191 \value Files List files.
1192 \value Drives List disk drives (ignored under Unix).
1193 \value NoSymLinks Do not list symbolic links (ignored by operating
1194 systems that don't support symbolic links).
1195 \value NoDotAndDotDot Do not list the special entries "." and "..".
1196 \value NoDot Do not list the special entry ".".
1197 \value NoDotDot Do not list the special entry "..".
1198 \value AllEntries List directories, files, drives and symlinks (this does not list
1199 broken symlinks unless you specify System).
1200 \value Readable List files for which the application has read
1201 access. The Readable value needs to be combined
1202 with Dirs or Files.
1203 \value Writable List files for which the application has write
1204 access. The Writable value needs to be combined
1205 with Dirs or Files.
1206 \value Executable List files for which the application has
1207 execute access. The Executable value needs to be
1208 combined with Dirs or Files.
1209 \value Modified Only list files that have been modified (ignored
1210 on Unix).
1211 \value Hidden List hidden files (on Unix, files starting with a ".").
1212 \value System List system files (on Unix, FIFOs, sockets and
1213 device files are included; on Windows, \c {.lnk}
1214 files are included)
1215 \value CaseSensitive The filter should be case sensitive.
1216
1217 \omitvalue TypeMask
1218 \omitvalue AccessMask
1219 \omitvalue PermissionMask
1220 \omitvalue NoFilter
1221
1222 Functions that use Filter enum values to filter lists of files
1223 and directories will include symbolic links to files and directories
1224 unless you set the NoSymLinks value.
1225
1226 A default constructed QDir will not filter out files based on
1227 their permissions, so entryList() and entryInfoList() will return
1228 all files that are readable, writable, executable, or any
1229 combination of the three. This makes the default easy to write,
1230 and at the same time useful.
1231
1232 For example, setting the \c Readable, \c Writable, and \c Files
1233 flags allows all files to be listed for which the application has read
1234 access, write access or both. If the \c Dirs and \c Drives flags are
1235 also included in this combination then all drives, directories, all
1236 files that the application can read, write, or execute, and symlinks
1237 to such files/directories can be listed.
1238
1239 To retrieve the permissions for a directory, use the
1240 entryInfoList() function to get the associated QFileInfo objects
1241 and then use the QFileInfo::permissions() to obtain the permissions
1242 and ownership for each file.
1243*/
1244
1245/*!
1246 Sets the filter used by entryList() and entryInfoList() to \a
1247 filters. The filter is used to specify the kind of files that
1248 should be returned by entryList() and entryInfoList(). See
1249 \l{QDir::Filter}.
1250
1251 \sa filter(), setNameFilters()
1252*/
1253void QDir::setFilter(Filters filters)
1254{
1255 Q_D(QDir);
1256 d->clearCache(mode: QDirPrivate::KeepMetaData);
1257 d->filters = filters;
1258}
1259
1260/*!
1261 Returns the value set by setSorting()
1262
1263 \sa setSorting(), SortFlag
1264*/
1265QDir::SortFlags QDir::sorting() const
1266{
1267 Q_D(const QDir);
1268 return d->sort;
1269}
1270
1271/*!
1272 \enum QDir::SortFlag
1273
1274 This enum describes the sort options available to QDir, e.g. for
1275 entryList() and entryInfoList(). The sort value is specified by
1276 OR-ing together values from the following list:
1277
1278 \value Name Sort by name.
1279 \value Time Sort by time (modification time).
1280 \value Size Sort by file size.
1281 \value Type Sort by file type (extension).
1282 \value Unsorted Do not sort.
1283 \value NoSort Not sorted by default.
1284
1285 \value DirsFirst Put the directories first, then the files.
1286 \value DirsLast Put the files first, then the directories.
1287 \value Reversed Reverse the sort order.
1288 \value IgnoreCase Sort case-insensitively.
1289 \value LocaleAware Sort items appropriately using the current locale settings.
1290
1291 \omitvalue SortByMask
1292
1293 You can only specify one of the first four.
1294
1295 If you specify both DirsFirst and Reversed, directories are
1296 still put first, but in reverse order; the files will be listed
1297 after the directories, again in reverse order.
1298*/
1299
1300#ifndef QT_BOOTSTRAPPED
1301/*!
1302 Sets the sort order used by entryList() and entryInfoList().
1303
1304 The \a sort is specified by OR-ing values from the enum
1305 \l{QDir::SortFlag}.
1306
1307 \sa sorting(), SortFlag
1308*/
1309void QDir::setSorting(SortFlags sort)
1310{
1311 Q_D(QDir);
1312 d->clearCache(mode: QDirPrivate::KeepMetaData);
1313 d->sort = sort;
1314}
1315
1316/*!
1317 Returns the total number of directories and files in the directory.
1318
1319 Equivalent to entryList().count().
1320
1321 \note In Qt versions prior to 6.5, this function returned \c{uint}, not
1322 \c{qsizetype}.
1323
1324 \sa operator[](), entryList()
1325*/
1326qsizetype QDir::count(QT6_IMPL_NEW_OVERLOAD) const
1327{
1328 Q_D(const QDir);
1329 d->initFileLists(dir: *this);
1330 return d->fileCache.files.size();
1331}
1332
1333/*!
1334 Returns the file name at position \a pos in the list of file
1335 names. Equivalent to entryList().at(index).
1336 \a pos must be a valid index position in the list (i.e., 0 <= pos < count()).
1337
1338 \note In Qt versions prior to 6.5, \a pos was an \c{int}, not \c{qsizetype}.
1339
1340 \sa count(), entryList()
1341*/
1342QString QDir::operator[](qsizetype pos) const
1343{
1344 Q_D(const QDir);
1345 d->initFileLists(dir: *this);
1346 return d->fileCache.files[pos];
1347}
1348
1349/*!
1350 \overload
1351
1352 Returns a list of the names of all the files and directories in
1353 the directory, ordered according to the name and attribute filters
1354 previously set with setNameFilters() and setFilter(), and sorted according
1355 to the flags set with setSorting().
1356
1357 The attribute filter and sorting specifications can be overridden using the
1358 \a filters and \a sort arguments.
1359
1360 Returns an empty list if the directory is unreadable, does not
1361 exist, or if nothing matches the specification.
1362
1363 \note To list symlinks that point to non existing files, \l System must be
1364 passed to the filter.
1365
1366 \sa entryInfoList(), setNameFilters(), setSorting(), setFilter()
1367*/
1368QStringList QDir::entryList(Filters filters, SortFlags sort) const
1369{
1370 Q_D(const QDir);
1371 return entryList(nameFilters: d->nameFilters, filters, sort);
1372}
1373
1374
1375/*!
1376 \overload
1377
1378 Returns a list of QFileInfo objects for all the files and directories in
1379 the directory, ordered according to the name and attribute filters
1380 previously set with setNameFilters() and setFilter(), and sorted according
1381 to the flags set with setSorting().
1382
1383 The attribute filter and sorting specifications can be overridden using the
1384 \a filters and \a sort arguments.
1385
1386 Returns an empty list if the directory is unreadable, does not
1387 exist, or if nothing matches the specification.
1388
1389 \sa entryList(), setNameFilters(), setSorting(), setFilter(), isReadable(), exists()
1390*/
1391QFileInfoList QDir::entryInfoList(Filters filters, SortFlags sort) const
1392{
1393 Q_D(const QDir);
1394 return entryInfoList(nameFilters: d->nameFilters, filters, sort);
1395}
1396
1397/*!
1398 Returns a list of the names of all the files and
1399 directories in the directory, ordered according to the name
1400 and attribute filters previously set with setNameFilters()
1401 and setFilter(), and sorted according to the flags set with
1402 setSorting().
1403
1404 The name filter, file attribute filter, and sorting specification
1405 can be overridden using the \a nameFilters, \a filters, and \a sort
1406 arguments.
1407
1408 Returns an empty list if the directory is unreadable, does not
1409 exist, or if nothing matches the specification.
1410
1411 \sa entryInfoList(), setNameFilters(), setSorting(), setFilter()
1412*/
1413QStringList QDir::entryList(const QStringList &nameFilters, Filters filters,
1414 SortFlags sort) const
1415{
1416 Q_D(const QDir);
1417
1418 if (filters == NoFilter)
1419 filters = d->filters;
1420 if (sort == NoSort)
1421 sort = d->sort;
1422
1423 const bool needsSorting = (sort & QDir::SortByMask) != QDir::Unsorted;
1424
1425 if (filters == d->filters && sort == d->sort && nameFilters == d->nameFilters) {
1426 // Don't fill a QFileInfo cache if we just need names
1427 if (needsSorting || d->fileCache.fileListsInitialized) {
1428 d->initFileLists(dir: *this);
1429 return d->fileCache.files;
1430 }
1431 }
1432
1433 QDirListing dirList(d->dirEntry.filePath(), nameFilters, filters.toInt());
1434 QStringList ret;
1435 if (needsSorting) {
1436 QFileInfoList l;
1437 for (const auto &dirEntry : dirList)
1438 l.emplace_back(args: dirEntry.fileInfo());
1439 d->sortFileList(sort, l, names: &ret, infos: nullptr);
1440 } else {
1441 for (const auto &dirEntry : dirList)
1442 ret.emplace_back(args: dirEntry.fileName());
1443 }
1444 return ret;
1445}
1446
1447/*!
1448 Returns a list of QFileInfo objects for all the files and
1449 directories in the directory, ordered according to the name
1450 and attribute filters previously set with setNameFilters()
1451 and setFilter(), and sorted according to the flags set with
1452 setSorting().
1453
1454 The name filter, file attribute filter, and sorting specification
1455 can be overridden using the \a nameFilters, \a filters, and \a sort
1456 arguments.
1457
1458 Returns an empty list if the directory is unreadable, does not
1459 exist, or if nothing matches the specification.
1460
1461 \sa entryList(), setNameFilters(), setSorting(), setFilter(), isReadable(), exists()
1462*/
1463QFileInfoList QDir::entryInfoList(const QStringList &nameFilters, Filters filters,
1464 SortFlags sort) const
1465{
1466 Q_D(const QDir);
1467
1468 if (filters == NoFilter)
1469 filters = d->filters;
1470 if (sort == NoSort)
1471 sort = d->sort;
1472
1473 if (filters == d->filters && sort == d->sort && nameFilters == d->nameFilters) {
1474 d->initFileLists(dir: *this);
1475 return d->fileCache.fileInfos;
1476 }
1477
1478 QFileInfoList l;
1479 for (const auto &dirEntry : QDirListing(d->dirEntry.filePath(), nameFilters, filters.toInt()))
1480 l.emplace_back(args: dirEntry.fileInfo());
1481 QFileInfoList ret;
1482 d->sortFileList(sort, l, names: nullptr, infos: &ret);
1483 return ret;
1484}
1485#endif // !QT_BOOTSTRAPPED
1486
1487/*!
1488 Creates a sub-directory called \a dirName.
1489
1490 Returns \c true on success; otherwise returns \c false.
1491
1492 If the directory already exists when this function is called, it will return \c false.
1493
1494 The permissions of the created directory are set to \a{permissions}.
1495
1496 On POSIX systems the permissions are influenced by the value of \c umask.
1497
1498 On Windows the permissions are emulated using ACLs. These ACLs may be in non-canonical
1499 order when the group is granted less permissions than others. Files and directories with
1500 such permissions will generate warnings when the Security tab of the Properties dialog
1501 is opened. Granting the group all permissions granted to others avoids such warnings.
1502
1503 \sa rmdir()
1504
1505 \since 6.3
1506*/
1507bool QDir::mkdir(const QString &dirName, QFile::Permissions permissions) const
1508{
1509 Q_D(const QDir);
1510
1511 if (dirName.isEmpty()) {
1512 qWarning(msg: "QDir::mkdir: Empty or null file name");
1513 return false;
1514 }
1515
1516 QString fn = filePath(fileName: dirName);
1517 if (!d->fileEngine)
1518 return QFileSystemEngine::createDirectory(entry: QFileSystemEntry(fn), createParents: false, permissions);
1519 return d->fileEngine->mkdir(dirName: fn, createParentDirectories: false, permissions);
1520}
1521
1522/*!
1523 \overload
1524 Creates a sub-directory called \a dirName with default permissions.
1525
1526 On POSIX systems the default is to grant all permissions allowed by \c umask.
1527 On Windows, the new directory inherits its permissions from its parent directory.
1528*/
1529bool QDir::mkdir(const QString &dirName) const
1530{
1531 Q_D(const QDir);
1532
1533 if (dirName.isEmpty()) {
1534 qWarning(msg: "QDir::mkdir: Empty or null file name");
1535 return false;
1536 }
1537
1538 QString fn = filePath(fileName: dirName);
1539 if (!d->fileEngine)
1540 return QFileSystemEngine::createDirectory(entry: QFileSystemEntry(fn), createParents: false);
1541 return d->fileEngine->mkdir(dirName: fn, createParentDirectories: false);
1542}
1543
1544/*!
1545 Removes the directory specified by \a dirName.
1546
1547 The directory must be empty for rmdir() to succeed.
1548
1549 Returns \c true if successful; otherwise returns \c false.
1550
1551 \sa mkdir()
1552*/
1553bool QDir::rmdir(const QString &dirName) const
1554{
1555 Q_D(const QDir);
1556
1557 if (dirName.isEmpty()) {
1558 qWarning(msg: "QDir::rmdir: Empty or null file name");
1559 return false;
1560 }
1561
1562 QString fn = filePath(fileName: dirName);
1563 if (!d->fileEngine)
1564 return QFileSystemEngine::removeDirectory(entry: QFileSystemEntry(fn), removeEmptyParents: false);
1565
1566 return d->fileEngine->rmdir(dirName: fn, recurseParentDirectories: false);
1567}
1568
1569/*!
1570 Creates the directory path \a dirPath.
1571
1572 The function will create all parent directories necessary to
1573 create the directory.
1574
1575 Returns \c true if successful; otherwise returns \c false.
1576
1577 If the path already exists when this function is called, it will return true.
1578
1579 \sa rmpath()
1580*/
1581bool QDir::mkpath(const QString &dirPath) const
1582{
1583 Q_D(const QDir);
1584
1585 if (dirPath.isEmpty()) {
1586 qWarning(msg: "QDir::mkpath: Empty or null file name");
1587 return false;
1588 }
1589
1590 QString fn = filePath(fileName: dirPath);
1591 if (!d->fileEngine)
1592 return QFileSystemEngine::createDirectory(entry: QFileSystemEntry(fn), createParents: true);
1593 return d->fileEngine->mkdir(dirName: fn, createParentDirectories: true);
1594}
1595
1596/*!
1597 Removes the directory path \a dirPath.
1598
1599 The function will remove all parent directories in \a dirPath,
1600 provided that they are empty. This is the opposite of
1601 mkpath(dirPath).
1602
1603 Returns \c true if successful; otherwise returns \c false.
1604
1605 \sa mkpath()
1606*/
1607bool QDir::rmpath(const QString &dirPath) const
1608{
1609 Q_D(const QDir);
1610
1611 if (dirPath.isEmpty()) {
1612 qWarning(msg: "QDir::rmpath: Empty or null file name");
1613 return false;
1614 }
1615
1616 QString fn = filePath(fileName: dirPath);
1617 if (!d->fileEngine)
1618 return QFileSystemEngine::removeDirectory(entry: QFileSystemEntry(fn), removeEmptyParents: true);
1619 return d->fileEngine->rmdir(dirName: fn, recurseParentDirectories: true);
1620}
1621
1622#ifndef QT_BOOTSTRAPPED
1623/*!
1624 \since 5.0
1625 Removes the directory, including all its contents.
1626
1627 Returns \c true if successful, otherwise false.
1628
1629 If a file or directory cannot be removed, removeRecursively() keeps going
1630 and attempts to delete as many files and sub-directories as possible,
1631 then returns \c false.
1632
1633 If the directory was already removed, the method returns \c true
1634 (expected result already reached).
1635
1636 \note This function is meant for removing a small application-internal
1637 directory (such as a temporary directory), but not user-visible
1638 directories. For user-visible operations, it is rather recommended
1639 to report errors more precisely to the user, to offer solutions
1640 in case of errors, to show progress during the deletion since it
1641 could take several minutes, etc.
1642*/
1643bool QDir::removeRecursively()
1644{
1645 if (!d_ptr->exists())
1646 return true;
1647
1648 bool success = true;
1649 const QString dirPath = path();
1650 // not empty -- we must empty it first
1651 for (const auto &dirEntry : QDirListing(dirPath, QDirListing::IteratorFlag::IncludeHidden)) {
1652 const QString &filePath = dirEntry.filePath();
1653 bool ok;
1654 if (dirEntry.isDir() && !dirEntry.isSymLink()) {
1655 ok = QDir(filePath).removeRecursively(); // recursive
1656 } else {
1657 ok = QFile::remove(fileName: filePath);
1658 if (!ok) { // Read-only files prevent directory deletion on Windows, retry with Write permission.
1659 const QFile::Permissions permissions = QFile::permissions(filename: filePath);
1660 if (!(permissions & QFile::WriteUser))
1661 ok = QFile::setPermissions(filename: filePath, permissionSpec: permissions | QFile::WriteUser)
1662 && QFile::remove(fileName: filePath);
1663 }
1664 }
1665 if (!ok)
1666 success = false;
1667 }
1668
1669 if (success)
1670 success = rmdir(dirName: absolutePath());
1671
1672 return success;
1673}
1674#endif // !QT_BOOTSTRAPPED
1675
1676/*!
1677 Returns \c true if the directory is readable \e and we can open files
1678 by name; otherwise returns \c false.
1679
1680 \warning A false value from this function is not a guarantee that
1681 files in the directory are not accessible.
1682
1683 \sa QFileInfo::isReadable()
1684*/
1685bool QDir::isReadable() const
1686{
1687 Q_D(const QDir);
1688
1689 if (!d->fileEngine) {
1690 QMutexLocker locker(&d->fileCache.mutex);
1691 if (!d->fileCache.metaData.hasFlags(flags: QFileSystemMetaData::UserReadPermission)) {
1692 QFileSystemEngine::fillMetaData(entry: d->dirEntry, data&: d->fileCache.metaData,
1693 what: QFileSystemMetaData::UserReadPermission);
1694 }
1695 return d->fileCache.metaData.permissions().testAnyFlag(flag: QFile::ReadUser);
1696 }
1697
1698 const QAbstractFileEngine::FileFlags info =
1699 d->fileEngine->fileFlags(type: QAbstractFileEngine::DirectoryType
1700 | QAbstractFileEngine::PermsMask);
1701 if (!(info & QAbstractFileEngine::DirectoryType))
1702 return false;
1703 return info.testAnyFlag(flag: QAbstractFileEngine::ReadUserPerm);
1704}
1705
1706/*!
1707 \overload
1708
1709 Returns \c true if the directory exists; otherwise returns \c false.
1710 (If a file with the same name is found this function will return false).
1711
1712 The overload of this function that accepts an argument is used to test
1713 for the presence of files and directories within a directory.
1714
1715 \sa QFileInfo::exists(), QFile::exists()
1716*/
1717bool QDir::exists() const
1718{
1719 return d_ptr->exists();
1720}
1721
1722/*!
1723 Returns \c true if the directory is the root directory; otherwise
1724 returns \c false.
1725
1726 \note If the directory is a symbolic link to the root directory
1727 this function returns \c false. If you want to test for this use
1728 canonicalPath(), e.g.
1729
1730 \snippet code/src_corelib_io_qdir.cpp 9
1731
1732 \sa root(), rootPath()
1733*/
1734bool QDir::isRoot() const
1735{
1736 if (!d_ptr->fileEngine)
1737 return d_ptr->dirEntry.isRoot();
1738 return d_ptr->fileEngine->fileFlags(type: QAbstractFileEngine::FlagsMask).testAnyFlag(flag: QAbstractFileEngine::RootFlag);
1739}
1740
1741/*!
1742 \fn bool QDir::isAbsolute() const
1743
1744 Returns \c true if the directory's path is absolute; otherwise
1745 returns \c false. See isAbsolutePath().
1746
1747 \note Paths starting with a colon (\e{:}) are always considered
1748 absolute, as they denote a QResource.
1749
1750 \sa isRelative(), makeAbsolute(), cleanPath()
1751*/
1752
1753/*!
1754 \fn bool QDir::isAbsolutePath(const QString &)
1755
1756 Returns \c true if \a path is absolute; returns \c false if it is
1757 relative.
1758
1759 \note Paths starting with a colon (\e{:}) are always considered
1760 absolute, as they denote a QResource.
1761
1762 \sa isAbsolute(), isRelativePath(), makeAbsolute(), cleanPath(), QResource
1763*/
1764
1765/*!
1766 Returns \c true if the directory path is relative; otherwise returns
1767 false. (Under Unix a path is relative if it does not start with a
1768 "/").
1769
1770 \note Paths starting with a colon (\e{:}) are always considered
1771 absolute, as they denote a QResource.
1772
1773 \sa makeAbsolute(), isAbsolute(), isAbsolutePath(), cleanPath()
1774*/
1775bool QDir::isRelative() const
1776{
1777 if (!d_ptr->fileEngine)
1778 return d_ptr->dirEntry.isRelative();
1779 return d_ptr->fileEngine->isRelativePath();
1780}
1781
1782
1783/*!
1784 Converts the directory path to an absolute path. If it is already
1785 absolute nothing happens. Returns \c true if the conversion
1786 succeeded; otherwise returns \c false.
1787
1788 \sa isAbsolute(), isAbsolutePath(), isRelative(), cleanPath()
1789*/
1790bool QDir::makeAbsolute()
1791{
1792 Q_D(const QDir);
1793 std::unique_ptr<QDirPrivate> dir;
1794 if (!!d->fileEngine) {
1795 QString absolutePath = d->fileEngine->fileName(file: QAbstractFileEngine::AbsoluteName);
1796 if (QDir::isRelativePath(path: absolutePath))
1797 return false;
1798
1799 dir.reset(p: new QDirPrivate(*d_ptr.constData()));
1800 dir->setPath(absolutePath);
1801 } else { // native FS
1802 QString absoluteFilePath = d->resolveAbsoluteEntry();
1803 dir.reset(p: new QDirPrivate(*d_ptr.constData()));
1804 dir->setPath(absoluteFilePath);
1805 }
1806 d_ptr = dir.release(); // actually detach
1807 return true;
1808}
1809
1810/*!
1811 \fn bool QDir::operator==(const QDir &lhs, const QDir &rhs)
1812
1813 Returns \c true if directory \a lhs and directory \a rhs have the same
1814 path and their sort and filter settings are the same; otherwise
1815 returns \c false.
1816
1817 Example:
1818
1819 \snippet code/src_corelib_io_qdir.cpp 10
1820*/
1821bool comparesEqual(const QDir &lhs, const QDir &rhs)
1822{
1823 const QDirPrivate *d = lhs.d_ptr.constData();
1824 const QDirPrivate *other = rhs.d_ptr.constData();
1825
1826 if (d == other)
1827 return true;
1828 Qt::CaseSensitivity sensitive;
1829 if (!d->fileEngine || !other->fileEngine) {
1830 if (d->fileEngine.get() != other->fileEngine.get()) // one is native, the other is a custom file-engine
1831 return false;
1832
1833 QOrderedMutexLocker locker(&d->fileCache.mutex, &other->fileCache.mutex);
1834 const bool thisCaseSensitive = QFileSystemEngine::isCaseSensitive(entry: d->dirEntry, data&: d->fileCache.metaData);
1835 if (thisCaseSensitive != QFileSystemEngine::isCaseSensitive(entry: other->dirEntry, data&: other->fileCache.metaData))
1836 return false;
1837
1838 sensitive = thisCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive;
1839 } else {
1840 if (d->fileEngine->caseSensitive() != other->fileEngine->caseSensitive())
1841 return false;
1842 sensitive = d->fileEngine->caseSensitive() ? Qt::CaseSensitive : Qt::CaseInsensitive;
1843 }
1844
1845 if (d->filters == other->filters
1846 && d->sort == other->sort
1847 && d->nameFilters == other->nameFilters) {
1848
1849 // Assume directories are the same if path is the same
1850 if (d->dirEntry.filePath() == other->dirEntry.filePath())
1851 return true;
1852
1853 if (lhs.exists()) {
1854 if (!rhs.exists())
1855 return false; //can't be equal if only one exists
1856 // Both exist, fallback to expensive canonical path computation
1857 return lhs.canonicalPath().compare(s: rhs.canonicalPath(), cs: sensitive) == 0;
1858 } else {
1859 if (rhs.exists())
1860 return false; //can't be equal if only one exists
1861 // Neither exists, compare absolute paths rather than canonical (which would be empty strings)
1862 QString thisFilePath = d->resolveAbsoluteEntry();
1863 QString otherFilePath = other->resolveAbsoluteEntry();
1864 return thisFilePath.compare(s: otherFilePath, cs: sensitive) == 0;
1865 }
1866 }
1867 return false;
1868}
1869
1870/*!
1871 Makes a copy of the \a dir object and assigns it to this QDir
1872 object.
1873*/
1874QDir &QDir::operator=(const QDir &dir)
1875{
1876 d_ptr = dir.d_ptr;
1877 return *this;
1878}
1879
1880/*!
1881 \fn void QDir::swap(QDir &other)
1882 \since 5.0
1883
1884 Swaps this QDir instance with \a other. This function is very fast
1885 and never fails.
1886*/
1887
1888/*!
1889 \fn bool QDir::operator!=(const QDir &lhs, const QDir &rhs)
1890
1891 Returns \c true if directory \a lhs and directory \a rhs have different
1892 paths or different sort or filter settings; otherwise returns \c false.
1893
1894 Example:
1895
1896 \snippet code/src_corelib_io_qdir.cpp 11
1897*/
1898
1899/*!
1900 Removes the file, \a fileName.
1901
1902 Returns \c true if the file is removed successfully; otherwise
1903 returns \c false.
1904*/
1905bool QDir::remove(const QString &fileName)
1906{
1907 if (fileName.isEmpty()) {
1908 qWarning(msg: "QDir::remove: Empty or null file name");
1909 return false;
1910 }
1911 return QFile::remove(fileName: filePath(fileName));
1912}
1913
1914/*!
1915 Renames a file or directory from \a oldName to \a newName, and returns
1916 true if successful; otherwise returns \c false.
1917
1918 On most file systems, rename() fails only if \a oldName does not
1919 exist, or if a file with the new name already exists.
1920 However, there are also other reasons why rename() can
1921 fail. For example, on at least one file system rename() fails if
1922 \a newName points to an open file.
1923
1924 If \a oldName is a file (not a directory) that can't be renamed
1925 right away, Qt will try to copy \a oldName to \a newName and remove
1926 \a oldName.
1927
1928 \sa QFile::rename()
1929*/
1930bool QDir::rename(const QString &oldName, const QString &newName)
1931{
1932 if (oldName.isEmpty() || newName.isEmpty()) {
1933 qWarning(msg: "QDir::rename: Empty or null file name(s)");
1934 return false;
1935 }
1936
1937 QFile file(filePath(fileName: oldName));
1938 if (!file.exists())
1939 return false;
1940 return file.rename(newName: filePath(fileName: newName));
1941}
1942
1943/*!
1944 Returns \c true if the file called \a name exists; otherwise returns
1945 false.
1946
1947 Unless \a name contains an absolute file path, the file name is assumed
1948 to be relative to the directory itself, so this function is typically used
1949 to check for the presence of files within a directory.
1950
1951 \sa QFileInfo::exists(), QFile::exists()
1952*/
1953bool QDir::exists(const QString &name) const
1954{
1955 if (name.isEmpty()) {
1956 qWarning(msg: "QDir::exists: Empty or null file name");
1957 return false;
1958 }
1959 return QFileInfo::exists(file: filePath(fileName: name));
1960}
1961
1962#ifndef QT_BOOTSTRAPPED
1963/*!
1964 Returns whether the directory is empty.
1965
1966 Equivalent to \c{count() == 0} with filters
1967 \c{QDir::AllEntries | QDir::NoDotAndDotDot}, but faster as it just checks
1968 whether the directory contains at least one entry.
1969
1970 \note Unless you set the \a filters flags to include \c{QDir::NoDotAndDotDot}
1971 (as the default value does), no directory is empty.
1972
1973 \sa count(), entryList(), setFilter()
1974 \since 5.9
1975*/
1976bool QDir::isEmpty(Filters filters) const
1977{
1978 Q_D(const QDir);
1979 QDirListing dirList(d->dirEntry.filePath(), d->nameFilters, filters.toInt());
1980 return dirList.cbegin() == dirList.cend();
1981}
1982#endif // !QT_BOOTSTRAPPED
1983
1984/*!
1985 Returns a list of the root directories on this system.
1986
1987 On Windows this returns a list of QFileInfo objects containing "C:/",
1988 "D:/", etc. This does not return drives with ejectable media that are empty.
1989 On other operating systems, it returns a list containing
1990 just one root directory (i.e. "/").
1991
1992 \sa root(), rootPath()
1993*/
1994QFileInfoList QDir::drives()
1995{
1996#ifdef QT_NO_FSFILEENGINE
1997 return QFileInfoList();
1998#else
1999 return QFSFileEngine::drives();
2000#endif
2001}
2002
2003/*!
2004 \fn QChar QDir::separator()
2005
2006 Returns the native directory separator: "/" under Unix
2007 and "\\" under Windows.
2008
2009 You do not need to use this function to build file paths. If you
2010 always use "/", Qt will translate your paths to conform to the
2011 underlying operating system. If you want to display paths to the
2012 user using their operating system's separator use
2013 toNativeSeparators().
2014
2015 \sa listSeparator()
2016*/
2017
2018/*!
2019 \fn QDir::listSeparator()
2020 \since 5.6
2021
2022 Returns the native path list separator: ':' under Unix
2023 and ';' under Windows.
2024
2025 \sa separator()
2026*/
2027
2028/*!
2029 Sets the application's current working directory to \a path.
2030 Returns \c true if the directory was successfully changed; otherwise
2031 returns \c false.
2032
2033 \snippet code/src_corelib_io_qdir.cpp 16
2034
2035 \sa current(), currentPath(), home(), root(), temp()
2036*/
2037bool QDir::setCurrent(const QString &path)
2038{
2039 return QFileSystemEngine::setCurrentPath(QFileSystemEntry(path));
2040}
2041
2042/*!
2043 \fn QDir QDir::current()
2044
2045 Returns the application's current directory.
2046
2047 The directory is constructed using the absolute path of the current directory,
2048 ensuring that its path() will be the same as its absolutePath().
2049
2050 \sa currentPath(), setCurrent(), home(), root(), temp()
2051*/
2052
2053/*!
2054 Returns the absolute path of the application's current directory. The
2055 current directory is the last directory set with QDir::setCurrent() or, if
2056 that was never called, the directory at which this application was started
2057 at by the parent process.
2058
2059 \sa current(), setCurrent(), homePath(), rootPath(), tempPath(), QCoreApplication::applicationDirPath()
2060*/
2061QString QDir::currentPath()
2062{
2063 return QFileSystemEngine::currentPath().filePath();
2064}
2065
2066/*!
2067 \fn QDir QDir::home()
2068
2069 Returns the user's home directory.
2070
2071 The directory is constructed using the absolute path of the home directory,
2072 ensuring that its path() will be the same as its absolutePath().
2073
2074 See homePath() for details.
2075
2076 \sa drives(), current(), root(), temp()
2077*/
2078
2079/*!
2080 Returns the absolute path of the user's home directory.
2081
2082 Under Windows this function will return the directory of the
2083 current user's profile. Typically, this is:
2084
2085 \snippet code/src_corelib_io_qdir.cpp 12
2086
2087 Use the toNativeSeparators() function to convert the separators to
2088 the ones that are appropriate for the underlying operating system.
2089
2090 If the directory of the current user's profile does not exist or
2091 cannot be retrieved, the following alternatives will be checked (in
2092 the given order) until an existing and available path is found:
2093
2094 \list 1
2095 \li The path specified by the \c USERPROFILE environment variable.
2096 \li The path formed by concatenating the \c HOMEDRIVE and \c HOMEPATH
2097 environment variables.
2098 \li The path specified by the \c HOME environment variable.
2099 \li The path returned by the rootPath() function (which uses the \c SystemDrive
2100 environment variable)
2101 \li The \c{C:/} directory.
2102 \endlist
2103
2104 Under non-Windows operating systems the \c HOME environment
2105 variable is used if it exists, otherwise the path returned by the
2106 rootPath().
2107
2108 \sa home(), currentPath(), rootPath(), tempPath()
2109*/
2110QString QDir::homePath()
2111{
2112 return QFileSystemEngine::homePath();
2113}
2114
2115/*!
2116 \fn QDir QDir::temp()
2117
2118 Returns the system's temporary directory.
2119
2120 The directory is constructed using the absolute canonical path of the temporary directory,
2121 ensuring that its path() will be the same as its absolutePath().
2122
2123 See tempPath() for details.
2124
2125 \sa drives(), current(), home(), root()
2126*/
2127
2128/*!
2129 Returns the absolute canonical path of the system's temporary directory.
2130
2131 On Unix/Linux systems this is the path in the \c TMPDIR environment
2132 variable or \c{/tmp} if \c TMPDIR is not defined. On Windows this is
2133 usually the path in the \c TEMP or \c TMP environment
2134 variable.
2135 The path returned by this method doesn't end with a directory separator
2136 unless it is the root directory (of a drive).
2137
2138 \sa temp(), currentPath(), homePath(), rootPath()
2139*/
2140QString QDir::tempPath()
2141{
2142 return QFileSystemEngine::tempPath();
2143}
2144
2145/*!
2146 \fn QDir QDir::root()
2147
2148 Returns the root directory.
2149
2150 The directory is constructed using the absolute path of the root directory,
2151 ensuring that its path() will be the same as its absolutePath().
2152
2153 See rootPath() for details.
2154
2155 \sa drives(), current(), home(), temp()
2156*/
2157
2158/*!
2159 Returns the absolute path of the root directory.
2160
2161 For Unix operating systems this returns "/". For Windows file
2162 systems this normally returns "c:/".
2163
2164 \sa root(), drives(), currentPath(), homePath(), tempPath()
2165*/
2166QString QDir::rootPath()
2167{
2168 return QFileSystemEngine::rootPath();
2169}
2170
2171#if QT_CONFIG(regularexpression)
2172/*!
2173 \overload
2174
2175 Returns \c true if the \a fileName matches any of the wildcard (glob)
2176 patterns in the list of \a filters; otherwise returns \c false. The
2177 matching is case insensitive.
2178
2179 \sa QRegularExpression::fromWildcard(), entryList(), entryInfoList()
2180*/
2181bool QDir::match(const QStringList &filters, const QString &fileName)
2182{
2183 for (QStringList::ConstIterator sit = filters.constBegin(); sit != filters.constEnd(); ++sit) {
2184 // Insensitive exact match
2185 auto rx = QRegularExpression::fromWildcard(pattern: *sit, cs: Qt::CaseInsensitive);
2186 if (rx.match(subject: fileName).hasMatch())
2187 return true;
2188 }
2189 return false;
2190}
2191
2192/*!
2193 Returns \c true if the \a fileName matches the wildcard (glob)
2194 pattern \a filter; otherwise returns \c false. The \a filter may
2195 contain multiple patterns separated by spaces or semicolons.
2196 The matching is case insensitive.
2197
2198 \sa QRegularExpression::fromWildcard(), entryList(), entryInfoList()
2199*/
2200bool QDir::match(const QString &filter, const QString &fileName)
2201{
2202 return match(filters: nameFiltersFromString(nameFilter: filter), fileName);
2203}
2204#endif // QT_CONFIG(regularexpression)
2205
2206/*!
2207 \internal
2208
2209 Updates \a path with redundant directory separators removed, and "."s and
2210 ".."s resolved (as far as possible). It returns \c false if there were ".."
2211 segments left over, attempt to go up past the root (only applies to
2212 absolute paths), or \c true otherwise.
2213
2214 This method is shared with QUrl, so it doesn't deal with QDir::separator(),
2215 nor does it remove the trailing slash, if any.
2216
2217 When dealing with URLs, we are following section 5.2.4 (Remove dot
2218 segments) from http://www.ietf.org/rfc/rfc3986.txt. URL mode differs from
2219 from local path mode in these ways:
2220 1) it can set *path to empty ("." becomes "")
2221 2) directory path outputs end in / ("a/.." becomes "a/" instead of "a")
2222 3) a sequence of "//" is treated as multiple path levels ("a/b//.." becomes
2223 "a/b/" and "a/b//../.." becomes "a/"), which matches the behavior
2224 observed in web browsers.
2225
2226 However, QUrl also uses local path mode for local URLs; with one exception:
2227 Even in local mode we leave one trailing slash for paths ending in "/." if
2228 the KeepLocalTrailingSlash flag is given. This reflects how QUrl needs to
2229 treat local URLs due to compatibility constraints.
2230*/
2231bool qt_normalizePathSegments(QString *path, QDirPrivate::PathNormalizations flags)
2232{
2233 const bool allowUncPaths = flags.testAnyFlag(flag: QDirPrivate::AllowUncPaths);
2234 const bool isRemote = flags.testAnyFlag(flag: QDirPrivate::RemotePath);
2235 const bool keepLocalTrailingSlash = flags.testAnyFlags(flags: QDirPrivate::KeepLocalTrailingSlash);
2236 const qsizetype prefixLength = rootLength(name: *path, allowUncPaths);
2237
2238 // RFC 3986 says: "The input buffer is initialized with the now-appended
2239 // path components and the output buffer is initialized to the empty
2240 // string."
2241 const QChar *in = path->constBegin();
2242
2243 // Scan the input for a "." or ".." segment. If there isn't any, we may not
2244 // need to modify this path at all. Also scan for "//" segments, which
2245 // will be normalized if the path is local.
2246 qsizetype i = prefixLength;
2247 qsizetype n = path->size();
2248 for (bool lastWasSlash = true; i < n; ++i) {
2249 if (lastWasSlash && in[i] == u'.') {
2250 if (i + 1 == n || in[i + 1] == u'/')
2251 break;
2252 if (in[i + 1] == u'.' && (i + 2 == n || in[i + 2] == u'/'))
2253 break;
2254 }
2255 if (!isRemote && lastWasSlash && in[i] == u'/' && i > 0) {
2256 // backtrack one, so the algorithm below gobbles up the remaining
2257 // slashes
2258 --i;
2259 break;
2260 }
2261 lastWasSlash = in[i] == u'/';
2262 }
2263 if (i == n)
2264 return true;
2265
2266 QChar *out = path->data(); // detaches
2267 const QChar *start = out + prefixLength;
2268 const QChar *end = out + path->size();
2269 out += i;
2270 in = out;
2271
2272 // We implement a modified algorithm compared to RFC 3986, for efficiency.
2273 bool ok = true;
2274 do {
2275#if 0 // to see in the debugger
2276 QString output = QStringView(path->constBegin(), out).toString();
2277 QStringView input(in, end);
2278#endif
2279
2280 // First, copy the preceding slashes, so we can look at the segment's
2281 // content. If the path is part of a URL, we copy all slashes, otherwise
2282 // just one.
2283 if (in[0] == u'/') {
2284 *out++ = *in++;
2285 while (in < end && in[0] == u'/') {
2286 if (isRemote)
2287 *out++ = *in++;
2288 else
2289 ++in;
2290
2291 // Note: we may exit this loop with in == end, in which case we
2292 // *shouldn't* dereference *in. But since we are pointing to a
2293 // detached, non-empty QString, we know there's a u'\0' at the
2294 // end, so dereferencing is safe.
2295 }
2296 }
2297
2298 // Is this path segment either "." or ".."?
2299 enum { Nothing, Dot, DotDot } type = Nothing;
2300 if (in[0] == u'.') {
2301 if (in + 1 == end || in[1] == u'/')
2302 type = Dot;
2303 else if (in[1] == u'.' && (in + 2 == end || in[2] == u'/'))
2304 type = DotDot;
2305 }
2306 if (type == Nothing) {
2307 // If it is neither, then we copy this segment.
2308 while (in < end && in[0] != u'/')
2309 *out++ = *in++;
2310 continue;
2311 }
2312
2313 // Otherwise, we skip it and remove preceding slashes (if
2314 // any, exactly one if part of a URL, all otherwise) from the
2315 // output. If it is "..", we remove the segment before that and
2316 // preceding slashes too in a similar fashion, if they are there.
2317 if (type == DotDot) {
2318 if (Q_UNLIKELY(out == start)) {
2319 // we can't go further up from here, so we "re-root"
2320 // without cleaning this segment
2321 ok = false;
2322 if (!isRemote) {
2323 *out++ = u'.';
2324 *out++ = u'.';
2325 if (in + 2 != end) {
2326 Q_ASSERT(in[2] == u'/');
2327 *out++ = u'/';
2328 ++in;
2329 }
2330 start = out;
2331 in += 2;
2332 continue;
2333 }
2334 }
2335 while (out > start && *--out != u'/')
2336 ;
2337 while (!isRemote && out > start && out[-1] == u'/')
2338 --out;
2339 while (out > start && out[-1] != u'/')
2340 --out;
2341 in += 2; // the two dots
2342 } else {
2343 ++in; // the one dot
2344 }
2345
2346 if (out > start) {
2347 // Always backtrack one slash
2348 if (out[-1] == u'/' && in != end)
2349 --out;
2350
2351 if (!isRemote) {
2352 bool removedAnySlashes = false;
2353
2354 // Backtrack all slashes ...
2355 while (out > start && out[-1] == u'/') {
2356 --out;
2357 removedAnySlashes = true;
2358 }
2359
2360 // ... except a trailing one if it exists and flag given
2361 if (removedAnySlashes && keepLocalTrailingSlash && out > start) {
2362 ++out;
2363 break;
2364 }
2365 }
2366 }
2367 if (out == start) {
2368 // We've reached the root. Make sure we don't turn a relative path
2369 // to absolute or, in the case of local paths that are already
2370 // absolute, into UNC.
2371 // Note: this will turn ".//a" into "a" even for URLs!
2372 if (in != end && in[0] == u'/')
2373 ++in;
2374 while (prefixLength == 0 && in != end && in[0] == u'/')
2375 ++in;
2376 }
2377 } while (in < end);
2378
2379 path->truncate(pos: out - path->constBegin());
2380 if (!isRemote && path->isEmpty())
2381 *path = u"."_s;
2382
2383 // we return false only if the path was absolute
2384 return ok || prefixLength == 0;
2385}
2386
2387static bool qt_cleanPath(QString *path)
2388{
2389 if (path->isEmpty())
2390 return true;
2391
2392 QString &ret = *path;
2393 ret = QDir::fromNativeSeparators(pathName: ret);
2394 auto normalization = OSSupportsUncPaths ? QDirPrivate::AllowUncPaths : QDirPrivate::DefaultNormalization;
2395 bool ok = qt_normalizePathSegments(path: &ret, flags: normalization);
2396
2397 // Strip away last slash except for root directories
2398 if (ret.size() > 1 && ret.endsWith(c: u'/')) {
2399#if defined (Q_OS_WIN)
2400 if (!(ret.length() == 3 && ret.at(1) == u':'))
2401#endif
2402 ret.chop(n: 1);
2403 }
2404
2405 return ok;
2406}
2407
2408/*!
2409 Returns \a path with directory separators normalized (that is, platform-native
2410 separators converted to "/") and redundant ones removed, and "."s and ".."s
2411 resolved (as far as possible).
2412
2413 Symbolic links are kept. This function does not return the
2414 canonical path, but rather the simplest version of the input.
2415 For example, "./local" becomes "local", "local/../bin" becomes
2416 "bin" and "/local/usr/../bin" becomes "/local/bin".
2417
2418 \sa absolutePath(), canonicalPath()
2419*/
2420QString QDir::cleanPath(const QString &path)
2421{
2422 QString ret = path;
2423 qt_cleanPath(path: &ret);
2424 return ret;
2425}
2426
2427/*!
2428 Returns \c true if \a path is relative; returns \c false if it is
2429 absolute.
2430
2431 \note Paths starting with a colon (\e{:}) are always considered
2432 absolute, as they denote a QResource.
2433
2434 \sa isRelative(), isAbsolutePath(), makeAbsolute()
2435*/
2436bool QDir::isRelativePath(const QString &path)
2437{
2438 return QFileInfo(path).isRelative();
2439}
2440
2441/*!
2442 Refreshes the directory information.
2443*/
2444void QDir::refresh() const
2445{
2446 QDirPrivate *d = const_cast<QDir *>(this)->d_func();
2447 d->clearCache(mode: QDirPrivate::IncludingMetaData);
2448}
2449
2450/*!
2451 \internal
2452*/
2453QDirPrivate* QDir::d_func()
2454{
2455 return d_ptr.data();
2456}
2457
2458/*!
2459 \internal
2460
2461 Returns a list of name filters from the given \a nameFilter. (If
2462 there is more than one filter, each pair of filters is separated
2463 by a space or by a semicolon.)
2464*/
2465QStringList QDir::nameFiltersFromString(const QString &nameFilter)
2466{
2467 return QDirPrivate::splitFilters(nameFilter);
2468}
2469
2470#ifndef QT_NO_DEBUG_STREAM
2471QDebug operator<<(QDebug debug, QDir::Filters filters)
2472{
2473 QDebugStateSaver save(debug);
2474 debug.resetFormat();
2475 QStringList flags;
2476 if (filters == QDir::NoFilter) {
2477 flags << "NoFilter"_L1;
2478 } else {
2479 if (filters & QDir::Dirs) flags << "Dirs"_L1;
2480 if (filters & QDir::AllDirs) flags << "AllDirs"_L1;
2481 if (filters & QDir::Files) flags << "Files"_L1;
2482 if (filters & QDir::Drives) flags << "Drives"_L1;
2483 if (filters & QDir::NoSymLinks) flags << "NoSymLinks"_L1;
2484 if (filters & QDir::NoDot) flags << "NoDot"_L1;
2485 if (filters & QDir::NoDotDot) flags << "NoDotDot"_L1;
2486 if ((filters & QDir::AllEntries) == QDir::AllEntries) flags << "AllEntries"_L1;
2487 if (filters & QDir::Readable) flags << "Readable"_L1;
2488 if (filters & QDir::Writable) flags << "Writable"_L1;
2489 if (filters & QDir::Executable) flags << "Executable"_L1;
2490 if (filters & QDir::Modified) flags << "Modified"_L1;
2491 if (filters & QDir::Hidden) flags << "Hidden"_L1;
2492 if (filters & QDir::System) flags << "System"_L1;
2493 if (filters & QDir::CaseSensitive) flags << "CaseSensitive"_L1;
2494 }
2495 debug.noquote() << "QDir::Filters(" << flags.join(sep: u'|') << ')';
2496 return debug;
2497}
2498
2499static QDebug operator<<(QDebug debug, QDir::SortFlags sorting)
2500{
2501 QDebugStateSaver save(debug);
2502 debug.resetFormat();
2503 if (sorting == QDir::NoSort) {
2504 debug << "QDir::SortFlags(NoSort)";
2505 } else {
2506 QString type;
2507 if ((sorting & QDir::SortByMask) == QDir::Name) type = "Name"_L1;
2508 if ((sorting & QDir::SortByMask) == QDir::Time) type = "Time"_L1;
2509 if ((sorting & QDir::SortByMask) == QDir::Size) type = "Size"_L1;
2510 if ((sorting & QDir::SortByMask) == QDir::Unsorted) type = "Unsorted"_L1;
2511
2512 QStringList flags;
2513 if (sorting & QDir::DirsFirst) flags << "DirsFirst"_L1;
2514 if (sorting & QDir::DirsLast) flags << "DirsLast"_L1;
2515 if (sorting & QDir::IgnoreCase) flags << "IgnoreCase"_L1;
2516 if (sorting & QDir::LocaleAware) flags << "LocaleAware"_L1;
2517 if (sorting & QDir::Type) flags << "Type"_L1;
2518 debug.noquote() << "QDir::SortFlags(" << type << '|' << flags.join(sep: u'|') << ')';
2519 }
2520 return debug;
2521}
2522
2523QDebug operator<<(QDebug debug, const QDir &dir)
2524{
2525 QDebugStateSaver save(debug);
2526 debug.resetFormat();
2527 debug << "QDir(" << dir.path() << ", nameFilters = {"
2528 << dir.nameFilters().join(sep: u',')
2529 << "}, "
2530 << dir.sorting()
2531 << ','
2532 << dir.filter()
2533 << ')';
2534 return debug;
2535}
2536#endif // QT_NO_DEBUG_STREAM
2537
2538/*!
2539 \fn QDir::QDir(const std::filesystem::path &path)
2540 \since 6.0
2541 Constructs a QDir pointing to the given directory \a path. If path
2542 is empty the program's working directory, ("."), is used.
2543
2544 \sa currentPath()
2545*/
2546/*!
2547 \fn QDir::QDir(const std::filesystem::path &path,
2548 const QString &nameFilter,
2549 SortFlags sort,
2550 Filters filters)
2551 \since 6.0
2552
2553 Constructs a QDir with path \a path, that filters its entries by
2554 name using \a nameFilter and by attributes using \a filters. It
2555 also sorts the names using \a sort.
2556
2557 The default \a nameFilter is an empty string, which excludes
2558 nothing; the default \a filters is \l AllEntries, which also
2559 excludes nothing. The default \a sort is \l Name | \l IgnoreCase,
2560 i.e. sort by name case-insensitively.
2561
2562 If \a path is empty, QDir uses "." (the current
2563 directory). If \a nameFilter is an empty string, QDir uses the
2564 name filter "*" (all files).
2565
2566 \note \a path need not exist.
2567
2568 \sa exists(), setPath(), setNameFilters(), setFilter(), setSorting()
2569*/
2570/*!
2571 \fn void QDir::setPath(const std::filesystem::path &path)
2572 \since 6.0
2573 \overload
2574*/
2575/*!
2576 \fn void QDir::addSearchPath(const QString &prefix, const std::filesystem::path &path)
2577 \since 6.0
2578 \overload
2579*/
2580/*!
2581 \fn std::filesystem::path QDir::filesystemPath() const
2582 \since 6.0
2583 Returns path() as \c{std::filesystem::path}.
2584 \sa path()
2585*/
2586/*!
2587 \fn std::filesystem::path QDir::filesystemAbsolutePath() const
2588 \since 6.0
2589 Returns absolutePath() as \c{std::filesystem::path}.
2590 \sa absolutePath()
2591*/
2592/*!
2593 \fn std::filesystem::path QDir::filesystemCanonicalPath() const
2594 \since 6.0
2595 Returns canonicalPath() as \c{std::filesystem::path}.
2596 \sa canonicalPath()
2597*/
2598
2599QT_END_NAMESPACE
2600

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