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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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