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

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