Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qtemporaryfile.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2017 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qtemporaryfile.h"
6
7#include "qplatformdefs.h"
8#include "qrandom.h"
9#include "private/qtemporaryfile_p.h"
10#include "private/qfile_p.h"
11#include "private/qsystemerror_p.h"
12
13#if !defined(Q_OS_WIN)
14#include "private/qcore_unix_p.h" // overrides QT_OPEN
15#include <errno.h>
16#endif
17
18#if defined(QT_BUILD_CORE_LIB)
19#include "qcoreapplication.h"
20#else
21#define tr(X) QString::fromLatin1(X)
22#endif
23
25
26using namespace Qt::StringLiterals;
27
28#if defined(Q_OS_WIN)
29typedef ushort Char;
30
31static inline Char Latin1Char(char ch)
32{
33 return ushort(uchar(ch));
34}
35
37
38#else // POSIX
39typedef char Char;
40typedef char Latin1Char;
41typedef int NativeFileHandle;
42#endif
43
45{
46 // Ensure there is a placeholder mask
47 QString qfilename = QDir::fromNativeSeparators(templateName);
48 qsizetype phPos = qfilename.size();
49 qsizetype phLength = 0;
50
51 while (phPos != 0) {
52 --phPos;
53
54 if (qfilename[phPos] == u'X') {
55 ++phLength;
56 continue;
57 }
58
59 if (phLength >= 6
60 || qfilename[phPos] == u'/') {
61 ++phPos;
62 break;
63 }
64
65 // start over
66 phLength = 0;
67 }
68
69 if (phLength < 6)
70 qfilename.append(".XXXXXX"_L1);
71
72 // "Nativify" :-)
75
76 // Find mask in native path
77 phPos = filename.size();
78 phLength = 0;
79 while (phPos != 0) {
80 --phPos;
81
82 if (filename[phPos] == Latin1Char('X')) {
83 ++phLength;
84 continue;
85 }
86
87 if (phLength >= 6) {
88 ++phPos;
89 break;
90 }
91
92 // start over
93 phLength = 0;
94 }
95
96 Q_ASSERT(phLength >= 6);
97 path = filename;
98 pos = phPos;
99 length = phLength;
100}
101
109{
110 Q_ASSERT(length != 0);
111 Q_ASSERT(pos < path.size());
112 Q_ASSERT(length <= path.size() - pos);
113
114 Char *const placeholderStart = (Char *)path.data() + pos;
115 Char *const placeholderEnd = placeholderStart + length;
116
117 // Replace placeholder with random chars.
118 {
119 // Since our dictionary is 26+26 characters, it would seem we only need
120 // a random number from 0 to 63 to select a character. However, due to
121 // the limited range, that would mean 12 (64-52) characters have double
122 // the probability of the others: 1 in 32 instead of 1 in 64.
123 //
124 // To overcome this limitation, we use more bits per character. With 10
125 // bits, there are 16 characters with probability 19/1024 and the rest
126 // at 20/1024 (i.e, less than .1% difference). This allows us to do 3
127 // characters per 32-bit random number, which is also half the typical
128 // placeholder length.
129 enum { BitsPerCharacter = 10 };
130
131 Char *rIter = placeholderEnd;
132 while (rIter != placeholderStart) {
133 quint32 rnd = QRandomGenerator::global()->generate();
134 auto applyOne = [&]() {
135 quint32 v = rnd & ((1 << BitsPerCharacter) - 1);
136 rnd >>= BitsPerCharacter;
137 char ch = char((26 + 26) * v / (1 << BitsPerCharacter));
138 if (ch < 26)
139 *--rIter = Latin1Char(ch + 'A');
140 else
141 *--rIter = Latin1Char(ch - 26 + 'a');
142 };
143
144 applyOne();
145 if (rIter == placeholderStart)
146 break;
147
148 applyOne();
149 if (rIter == placeholderStart)
150 break;
151
152 applyOne();
153 }
154 }
155
156 return path;
157}
158
159#if QT_CONFIG(temporaryfile)
160
174static bool createFileFromTemplate(NativeFileHandle &file, QTemporaryFileName &templ,
176{
177 const int maxAttempts = 16;
178 for (int attempt = 0; attempt < maxAttempts; ++attempt) {
179 // Atomically create file and obtain handle
180 const QFileSystemEntry::NativePath &path = templ.generateNext();
181
182#if defined(Q_OS_WIN)
183 Q_UNUSED(mode);
184 const DWORD shareMode = (flags & QTemporaryFileEngine::Win32NonShared)
185 ? 0u : (FILE_SHARE_READ | FILE_SHARE_WRITE);
186
187 const DWORD extraAccessFlags = (flags & QTemporaryFileEngine::Win32NonShared) ? DELETE : 0;
188 file = CreateFile((const wchar_t *)path.constData(),
189 GENERIC_READ | GENERIC_WRITE | extraAccessFlags,
190 shareMode, NULL, CREATE_NEW,
191 FILE_ATTRIBUTE_NORMAL, NULL);
192
193 if (file != INVALID_HANDLE_VALUE)
194 return true;
195
196 DWORD err = GetLastError();
197 if (err == ERROR_ACCESS_DENIED) {
198 WIN32_FILE_ATTRIBUTE_DATA attributes;
199 if (!GetFileAttributesEx((const wchar_t *)path.constData(),
200 GetFileExInfoStandard, &attributes)
201 || attributes.dwFileAttributes == INVALID_FILE_ATTRIBUTES) {
202 // Potential write error (read-only parent directory, etc.).
204 return false;
205 } // else file already exists as a directory.
206 } else if (err != ERROR_FILE_EXISTS) {
208 return false;
209 }
210#else // POSIX
212 file = QT_OPEN(path.constData(),
213 QT_OPEN_CREAT | QT_OPEN_EXCL | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
214 static_cast<mode_t>(mode));
215
216 if (file != -1)
217 return true;
218
219 int err = errno;
220 if (err != EEXIST) {
222 return false;
223 }
224#endif
225 }
226
227 return false;
228}
229
230enum class CreateUnnamedFileStatus {
231 Success = 0,
232 NotSupported,
233 OtherError
234};
235
236static CreateUnnamedFileStatus
238{
239#ifdef LINUX_UNNAMED_TMPFILE
240 // first, check if we have /proc, otherwise can't make the file exist later
241 // (no error message set, as caller will try regular temporary file)
242 if (!qt_haveLinuxProcfs())
243 return CreateUnnamedFileStatus::NotSupported;
244
245 const char *p = ".";
246 QByteArray::size_type lastSlash = tfn.path.lastIndexOf('/');
247 if (lastSlash >= 0) {
248 if (lastSlash == 0)
249 lastSlash = 1;
250 tfn.path[lastSlash] = '\0';
251 p = tfn.path.data();
252 }
253
254 file = QT_OPEN(p, O_TMPFILE | QT_OPEN_RDWR | QT_OPEN_LARGEFILE,
255 static_cast<mode_t>(mode));
256 if (file != -1)
257 return CreateUnnamedFileStatus::Success;
258
259 if (errno == EOPNOTSUPP || errno == EISDIR) {
260 // fs or kernel doesn't support O_TMPFILE, so
261 // put the slash back so we may try a regular file
262 if (lastSlash != -1)
263 tfn.path[lastSlash] = '/';
264 return CreateUnnamedFileStatus::NotSupported;
265 }
266
267 // real error
269 return CreateUnnamedFileStatus::OtherError;
270#else
271 Q_UNUSED(file);
272 Q_UNUSED(tfn);
273 Q_UNUSED(mode);
275 return CreateUnnamedFileStatus::NotSupported;
276#endif
277}
278
279//************* QTemporaryFileEngine
280QTemporaryFileEngine::~QTemporaryFileEngine()
281{
282 Q_D(QFSFileEngine);
283 d->unmapAll();
285}
286
287bool QTemporaryFileEngine::isReallyOpen() const
288{
289 Q_D(const QFSFileEngine);
290
291 if (!((nullptr == d->fh) && (-1 == d->fd)
292#if defined Q_OS_WIN
293 && (INVALID_HANDLE_VALUE == d->fileHandle)
294#endif
295 ))
296 return true;
297
298 return false;
299
300}
301
302void QTemporaryFileEngine::setFileName(const QString &file)
303{
304 // Really close the file, so we don't leak
307}
308
309bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode,
310 std::optional<QFile::Permissions> permissions)
311{
312 Q_D(QFSFileEngine);
313 Q_ASSERT(!isReallyOpen());
314
315 openMode |= QIODevice::ReadWrite;
316
317 if (!filePathIsTemplate)
318 return QFSFileEngine::open(openMode, permissions);
319
320 QTemporaryFileName tfn(templateName);
321
323#if defined(Q_OS_WIN)
324 NativeFileHandle &file = d->fileHandle;
325#else // POSIX
326 NativeFileHandle &file = d->fd;
327#endif
328
329 CreateUnnamedFileStatus st = createUnnamedFile(file, tfn, fileMode, &error);
330 if (st == CreateUnnamedFileStatus::Success) {
331 unnamedFile = true;
332 d->fileEntry.clear();
333 } else if (st == CreateUnnamedFileStatus::NotSupported &&
334 createFileFromTemplate(file, tfn, fileMode, flags, error)) {
335 filePathIsTemplate = false;
336 unnamedFile = false;
337 d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
338 } else {
339 setError(QFile::OpenError, error.toString());
340 return false;
341 }
342
343#if !defined(Q_OS_WIN)
344 d->closeFileHandle = true;
345#endif
346
347 d->openMode = openMode;
348 d->lastFlushFailed = false;
349 d->tried_stat = 0;
350
351 return true;
352}
353
354bool QTemporaryFileEngine::remove()
355{
356 Q_D(QFSFileEngine);
357 // Since the QTemporaryFileEngine::close() does not really close the file,
358 // we must explicitly call QFSFileEngine::close() before we remove it.
359 d->unmapAll();
361 if (isUnnamedFile())
362 return true;
363 if (!filePathIsTemplate && QFSFileEngine::remove()) {
364 d->fileEntry.clear();
365 // If a QTemporaryFile is constructed using a template file path, the path
366 // is generated in QTemporaryFileEngine::open() and then filePathIsTemplate
367 // is set to false. If remove() and then open() are called on the same
368 // QTemporaryFile, the path is not regenerated. Here we ensure that if the
369 // file path was generated, it will be generated again in the scenario above.
370 filePathIsTemplate = filePathWasTemplate;
371 return true;
372 }
373 return false;
374}
375
376bool QTemporaryFileEngine::rename(const QString &newName)
377{
378 if (isUnnamedFile()) {
379 bool ok = materializeUnnamedFile(newName, DontOverwrite);
381 return ok;
382 }
384 return QFSFileEngine::rename(newName);
385}
386
387bool QTemporaryFileEngine::renameOverwrite(const QString &newName)
388{
389 if (isUnnamedFile()) {
390 bool ok = materializeUnnamedFile(newName, Overwrite);
392 return ok;
393 }
394#ifdef Q_OS_WIN
395 if (flags & Win32NonShared) {
397 bool ok = d_func()->nativeRenameOverwrite(newEntry);
399 if (ok) {
400 // Match what QFSFileEngine::renameOverwrite() does
401 setFileEntry(std::move(newEntry));
402 }
403 return ok;
404 }
405#endif
407 return QFSFileEngine::renameOverwrite(newName);
408}
409
410bool QTemporaryFileEngine::close()
411{
412 // Don't close the file, just seek to the front.
413 seek(0);
415 return true;
416}
417
418QString QTemporaryFileEngine::fileName(QAbstractFileEngine::FileName file) const
419{
420 if (isUnnamedFile()) {
421 if (file == AbsoluteLinkTarget || file == RawLinkPath) {
422 // we know our file isn't (won't be) a symlink
423 return QString();
424 }
425
426 // for all other cases, materialize the file
427 const_cast<QTemporaryFileEngine *>(this)->materializeUnnamedFile(templateName, NameIsTemplate);
428 }
430}
431
432bool QTemporaryFileEngine::materializeUnnamedFile(const QString &newName, QTemporaryFileEngine::MaterializationMode mode)
433{
434 Q_ASSERT(isUnnamedFile());
435
436#ifdef LINUX_UNNAMED_TMPFILE
437 Q_D(QFSFileEngine);
438 const QByteArray src = "/proc/self/fd/" + QByteArray::number(d->fd);
439 auto materializeAt = [=](const QFileSystemEntry &dst) {
440 return ::linkat(AT_FDCWD, src, AT_FDCWD, dst.nativeFilePath(), AT_SYMLINK_FOLLOW) == 0;
441 };
442#else
443 auto materializeAt = [](const QFileSystemEntry &) { return false; };
444#endif
445
446 auto success = [this](const QFileSystemEntry &entry) {
447 filePathIsTemplate = false;
448 unnamedFile = false;
449 d_func()->fileEntry = entry;
450 return true;
451 };
452
453 auto materializeAsTemplate = [=](const QString &newName) {
454 QTemporaryFileName tfn(newName);
455 static const int maxAttempts = 16;
456 for (int attempt = 0; attempt < maxAttempts; ++attempt) {
457 tfn.generateNext();
459 if (materializeAt(entry))
460 return success(entry);
461 }
462 return false;
463 };
464
465 if (mode == NameIsTemplate) {
466 if (materializeAsTemplate(newName))
467 return true;
468 } else {
469 // Use linkat to materialize the file
470 QFileSystemEntry dst(newName);
471 if (materializeAt(dst))
472 return success(dst);
473
474 if (errno == EEXIST && mode == Overwrite) {
475 // retry by first creating a temporary file in the right dir
476 if (!materializeAsTemplate(templateName))
477 return false;
478
479 // then rename the materialized file to target (same as renameOverwrite)
481 return QFSFileEngine::renameOverwrite(newName);
482 }
483 }
484
485 // failed
487 return false;
488}
489
490bool QTemporaryFileEngine::isUnnamedFile() const
491{
492#ifdef LINUX_UNNAMED_TMPFILE
493 if (unnamedFile) {
494 Q_ASSERT(d_func()->fileEntry.isEmpty());
495 Q_ASSERT(filePathIsTemplate);
496 }
497 return unnamedFile;
498#else
499 return false;
500#endif
501}
502
503//************* QTemporaryFilePrivate
504
505QTemporaryFilePrivate::QTemporaryFilePrivate()
506{
507}
508
509QTemporaryFilePrivate::QTemporaryFilePrivate(const QString &templateNameIn)
510 : templateName(templateNameIn)
511{
512}
513
514QTemporaryFilePrivate::~QTemporaryFilePrivate()
515{
516}
517
518QAbstractFileEngine *QTemporaryFilePrivate::engine() const
519{
520 if (!fileEngine) {
521 fileEngine.reset(new QTemporaryFileEngine(&templateName));
522 resetFileEngine();
523 }
524 return fileEngine.get();
525}
526
527void QTemporaryFilePrivate::resetFileEngine() const
528{
529 if (!fileEngine)
530 return;
531
532 QTemporaryFileEngine *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
533 if (fileName.isEmpty())
534 tef->initialize(templateName, 0600);
535 else
536 tef->initialize(fileName, 0600, false);
537}
538
539void QTemporaryFilePrivate::materializeUnnamedFile()
540{
541#ifdef LINUX_UNNAMED_TMPFILE
542 if (!fileName.isEmpty() || !fileEngine)
543 return;
544
545 auto *tef = static_cast<QTemporaryFileEngine *>(fileEngine.get());
547#endif
548}
549
550QString QTemporaryFilePrivate::defaultTemplateName()
551{
552 QString baseName;
553#if defined(QT_BUILD_CORE_LIB)
555 if (baseName.isEmpty())
556#endif
557 baseName = "qt_temp"_L1;
558
559 return QDir::tempPath() + u'/' + baseName + ".XXXXXX"_L1;
560}
561
562//************* QTemporaryFile
563
620#ifdef QT_NO_QOBJECT
621QTemporaryFile::QTemporaryFile()
622 : QFile(*new QTemporaryFilePrivate)
623{
624}
625
626QTemporaryFile::QTemporaryFile(const QString &templateName)
627 : QFile(*new QTemporaryFilePrivate(templateName))
628{
629}
630
631#else
640QTemporaryFile::QTemporaryFile()
641 : QTemporaryFile(nullptr)
642{
643}
644
669QTemporaryFile::QTemporaryFile(const QString &templateName)
670 : QTemporaryFile(templateName, nullptr)
671{
672}
673
682QTemporaryFile::QTemporaryFile(QObject *parent)
683 : QFile(*new QTemporaryFilePrivate, parent)
684{
685}
686
705QTemporaryFile::QTemporaryFile(const QString &templateName, QObject *parent)
706 : QFile(*new QTemporaryFilePrivate(templateName), parent)
707{
708}
709#endif
710
718QTemporaryFile::~QTemporaryFile()
719{
720 Q_D(QTemporaryFile);
721 close();
722 if (!d->fileName.isEmpty() && d->autoRemove)
723 remove();
724}
725
749bool QTemporaryFile::autoRemove() const
750{
751 Q_D(const QTemporaryFile);
752 return d->autoRemove;
753}
754
773void QTemporaryFile::setAutoRemove(bool b)
774{
775 Q_D(QTemporaryFile);
776 d->autoRemove = b;
777}
778
788QString QTemporaryFile::fileName() const
789{
790 Q_D(const QTemporaryFile);
791 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
792 if (tef && tef->isReallyOpen())
793 const_cast<QTemporaryFilePrivate *>(d)->materializeUnnamedFile();
794
795 if (d->fileName.isEmpty())
796 return QString();
797 return d->engine()->fileName(QAbstractFileEngine::DefaultName);
798}
799
806QString QTemporaryFile::fileTemplate() const
807{
808 Q_D(const QTemporaryFile);
809 return d->templateName;
810}
811
833void QTemporaryFile::setFileTemplate(const QString &name)
834{
835 Q_D(QTemporaryFile);
836 d->templateName = name;
837}
838
863bool QTemporaryFile::rename(const QString &newName)
864{
865 Q_D(QTemporaryFile);
866 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
867 if (!tef || !tef->isReallyOpen() || !tef->filePathWasTemplate)
868 return QFile::rename(newName);
869
870 unsetError();
871 close();
872 if (error() == QFile::NoError) {
873 if (tef->rename(newName)) {
874 unsetError();
875 // engine was able to handle the new name so we just reset it
876 d->fileName = newName;
877 return true;
878 }
879
880 d->setError(QFile::RenameError, tef->errorString());
881 }
882 return false;
883}
884
911QTemporaryFile *QTemporaryFile::createNativeFile(QFile &file)
912{
913 if (QAbstractFileEngine *engine = file.d_func()->engine()) {
915 return nullptr; // native already
916 //cache
917 bool wasOpen = file.isOpen();
918 qint64 old_off = 0;
919 if (wasOpen)
920 old_off = file.pos();
921 else if (!file.open(QIODevice::ReadOnly))
922 return nullptr;
923 //dump data
924 QTemporaryFile *ret = new QTemporaryFile;
925 if (ret->open()) {
926 file.seek(0);
927 char buffer[1024];
928 while (true) {
929 qint64 len = file.read(buffer, 1024);
930 if (len < 1)
931 break;
932 ret->write(buffer, len);
933 }
934 ret->seek(0);
935 } else {
936 delete ret;
937 ret = nullptr;
938 }
939 //restore
940 if (wasOpen)
941 file.seek(old_off);
942 else
943 file.close();
944 //done
945 return ret;
946 }
947 return nullptr;
948}
949
957bool QTemporaryFile::open(OpenMode flags)
958{
959 Q_D(QTemporaryFile);
960 auto tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
961 if (tef && tef->isReallyOpen()) {
962 setOpenMode(flags);
963 return true;
964 }
965
966 // reset the engine state so it creates a new, unique file name from the template;
967 // equivalent to:
968 // delete d->fileEngine;
969 // d->fileEngine = 0;
970 // d->engine();
971 d->resetFileEngine();
972
973 if (QFile::open(flags)) {
974 tef = static_cast<QTemporaryFileEngine *>(d->fileEngine.get());
975 if (tef->isUnnamedFile())
976 d->fileName.clear();
977 else
978 d->fileName = tef->fileName(QAbstractFileEngine::DefaultName);
979 return true;
980 }
981 return false;
982}
983
984#endif // QT_CONFIG(temporaryfile)
985
987
988#ifndef QT_NO_QOBJECT
989#include "moc_qtemporaryfile.cpp"
990#endif
\inmodule QtCore \reentrant
FileName
These values are used to request a file name in a particular format.
\inmodule QtCore
Definition qbytearray.h:57
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:494
qsizetype size_type
Definition qbytearray.h:459
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QString applicationName
the name of this application
static QString fromNativeSeparators(const QString &pathName)
Definition qdir.cpp:962
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2133
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
\inmodule QtCore
QString fileName(FileName file) const override
\reimp
bool rename(const QString &newName) override
Requests that the file be renamed to newName in the file system.
bool renameOverwrite(const QString &newName) override
bool close() override
\reimp
bool open(QIODevice::OpenMode openMode, std::optional< QFile::Permissions > permissions) override
\reimp
void setFileName(const QString &file) override
\reimp
bool remove() override
\reimp
qint64 pos() const override
\reimp
bool seek(qint64 offset) override
For random-access devices, this function sets the current position to pos, returning true on success,...
void close() override
Calls QFileDevice::flush() and closes the file.
Q_AUTOTEST_EXPORT NativePath nativeFilePath() const
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
bool rename(const QString &newName)
Renames the file currently specified by fileName() to newName.
Definition qfile.cpp:554
bool isOpen() const
Returns true if the device is open; otherwise returns false.
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read.
\inmodule QtCore
Definition qobject.h:103
static Q_DECL_CONST_FUNCTION QRandomGenerator * global()
\threadsafe
Definition qrandom.h:275
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
Combined button and popup list for selecting options.
void * HANDLE
#define QT_OPEN
bool qt_haveLinuxProcfs()
DBusConnection const char DBusError * error
#define INVALID_FILE_ATTRIBUTES
return ret
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLenum mode
GLenum GLuint GLenum GLsizei length
GLenum src
GLenum GLuint buffer
GLenum GLenum dst
GLbitfield flags
GLuint name
GLuint entry
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
GLenum GLsizei len
static void setError(QJsonObject *response, const QString &msg)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
@ Success
Definition main.cpp:3325
char Char
int NativeFileHandle
char Latin1Char
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned char uchar
Definition qtypes.h:32
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
unsigned short ushort
Definition qtypes.h:33
QFile file
[0]
settings remove("monkey")
QObject::connect nullptr
char * toString(const MyType &t)
[31]
QJSEngine engine
[0]
QTemporaryFileName(const QString &templateName)
QFileSystemEntry::NativePath generateNext()
QFileSystemEntry::NativePath path