Long live QTemporaryFileName!
So we can use it in QTemporaryFile, QTemporaryDir and QFile::rename() [ChangeLog][QtCore][QTemporaryDir] The class now supports the "XXXXXX" replacement token anywhere in the template, not just at the end. This behavior is similar to what QTemporaryFile supports. Change-Id: I1eba2b016de74620bfc8fffd14ccb645729de170 Reviewed-by: Lars Knoll <lars.knoll@qt.io>bb10
parent
3981a1f440
commit
3d6cbe3409
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Intel Corporation.
|
||||
** Copyright (C) 2017 Intel Corporation.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
|
|
@ -44,22 +44,15 @@
|
|||
|
||||
#include "qdiriterator.h"
|
||||
#include "qplatformdefs.h"
|
||||
#include "private/qtemporaryfile_p.h"
|
||||
#include <QDebug>
|
||||
#include <QPair>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#if defined(QT_BUILD_CORE_LIB)
|
||||
#include "qcoreapplication.h"
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_QNX) && !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_INTEGRITY)
|
||||
# define USE_SYSTEM_MKDTEMP
|
||||
#endif
|
||||
|
||||
#include <stdlib.h> // mkdtemp
|
||||
#ifndef USE_SYSTEM_MKDTEMP
|
||||
#include <private/qfilesystemengine_p.h>
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_WIN)
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
|
@ -102,38 +95,12 @@ static QString defaultTemplateName()
|
|||
return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String("-XXXXXX");
|
||||
}
|
||||
|
||||
#ifndef USE_SYSTEM_MKDTEMP
|
||||
static int nextRand(int &v)
|
||||
void QTemporaryDirPrivate::create(const QString &templateName)
|
||||
{
|
||||
int r = v % 62;
|
||||
v /= 62;
|
||||
if (v < 62)
|
||||
v = qrand();
|
||||
return r;
|
||||
}
|
||||
|
||||
QPair<QString, bool> q_mkdtemp(QString templateName)
|
||||
{
|
||||
Q_ASSERT(templateName.endsWith(QLatin1String("XXXXXX")));
|
||||
|
||||
static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
const int length = templateName.size();
|
||||
|
||||
QChar *XXXXXX = templateName.data() + length - 6;
|
||||
|
||||
QTemporaryFileName tfn(templateName);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
int v = qrand();
|
||||
|
||||
/* Fill in the random bits. */
|
||||
XXXXXX[0] = QLatin1Char(letters[nextRand(v)]);
|
||||
XXXXXX[1] = QLatin1Char(letters[nextRand(v)]);
|
||||
XXXXXX[2] = QLatin1Char(letters[nextRand(v)]);
|
||||
XXXXXX[3] = QLatin1Char(letters[nextRand(v)]);
|
||||
XXXXXX[4] = QLatin1Char(letters[nextRand(v)]);
|
||||
XXXXXX[5] = QLatin1Char(letters[v % 62]);
|
||||
|
||||
QFileSystemEntry fileSystemEntry(templateName);
|
||||
tfn.generateNext();
|
||||
QFileSystemEntry fileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
|
||||
if (QFileSystemEngine::createDirectory(fileSystemEntry, false)) {
|
||||
QSystemError error;
|
||||
QFileSystemEngine::setPermissions(fileSystemEntry,
|
||||
|
|
@ -145,7 +112,9 @@ QPair<QString, bool> q_mkdtemp(QString templateName)
|
|||
qWarning() << "Unable to remove unused directory" << templateName;
|
||||
continue;
|
||||
}
|
||||
return qMakePair(templateName, true);
|
||||
success = true;
|
||||
pathOrError = fileSystemEntry.filePath();
|
||||
return;
|
||||
}
|
||||
# ifdef Q_OS_WIN
|
||||
const int exists = ERROR_ALREADY_EXISTS;
|
||||
|
|
@ -155,36 +124,10 @@ QPair<QString, bool> q_mkdtemp(QString templateName)
|
|||
int code = errno;
|
||||
# endif
|
||||
if (code != exists)
|
||||
return qMakePair(qt_error_string(code), false);
|
||||
break;
|
||||
}
|
||||
return qMakePair(qt_error_string(), false);
|
||||
}
|
||||
|
||||
#else // !USE_SYSTEM_MKDTEMP
|
||||
|
||||
QPair<QString, bool> q_mkdtemp(char *templateName)
|
||||
{
|
||||
bool ok = (mkdtemp(templateName) != 0);
|
||||
return qMakePair(ok ? QFile::decodeName(templateName) : qt_error_string(), ok);
|
||||
}
|
||||
|
||||
#endif // USE_SYSTEM_MKDTEMP
|
||||
|
||||
void QTemporaryDirPrivate::create(const QString &templateName)
|
||||
{
|
||||
#ifndef USE_SYSTEM_MKDTEMP
|
||||
QString buffer = templateName;
|
||||
if (!buffer.endsWith(QLatin1String("XXXXXX")))
|
||||
buffer += QLatin1String("XXXXXX");
|
||||
const QPair<QString, bool> result = q_mkdtemp(buffer);
|
||||
#else // !USE_SYSTEM_MKDTEMP
|
||||
QByteArray buffer = QFile::encodeName(templateName);
|
||||
if (!buffer.endsWith("XXXXXX"))
|
||||
buffer += "XXXXXX";
|
||||
QPair<QString, bool> result = q_mkdtemp(buffer.data()); // modifies buffer
|
||||
#endif // USE_SYSTEM_MKDTEMP
|
||||
pathOrError = result.first;
|
||||
success = result.second;
|
||||
pathOrError = qt_error_string();
|
||||
success = false;
|
||||
}
|
||||
|
||||
//************* QTemporaryDir
|
||||
|
|
|
|||
|
|
@ -39,8 +39,6 @@
|
|||
|
||||
#include "qtemporaryfile.h"
|
||||
|
||||
#ifndef QT_NO_TEMPORARYFILE
|
||||
|
||||
#include "qplatformdefs.h"
|
||||
#include "qrandom.h"
|
||||
#include "private/qtemporaryfile_p.h"
|
||||
|
|
@ -76,12 +74,63 @@ typedef char Latin1Char;
|
|||
typedef int NativeFileHandle;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
struct NativeTemplate {
|
||||
QFileSystemEntry::NativePath path;
|
||||
qssize_t pos;
|
||||
qssize_t length;
|
||||
};
|
||||
QTemporaryFileName::QTemporaryFileName(const QString &templateName)
|
||||
{
|
||||
// Ensure there is a placeholder mask
|
||||
QString qfilename = templateName;
|
||||
uint phPos = qfilename.length();
|
||||
uint phLength = 0;
|
||||
|
||||
while (phPos != 0) {
|
||||
--phPos;
|
||||
|
||||
if (qfilename[phPos] == QLatin1Char('X')) {
|
||||
++phLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phLength >= 6
|
||||
|| qfilename[phPos] == QLatin1Char('/')) {
|
||||
++phPos;
|
||||
break;
|
||||
}
|
||||
|
||||
// start over
|
||||
phLength = 0;
|
||||
}
|
||||
|
||||
if (phLength < 6)
|
||||
qfilename.append(QLatin1String(".XXXXXX"));
|
||||
|
||||
// "Nativify" :-)
|
||||
QFileSystemEntry::NativePath filename = QFileSystemEngine::absoluteName(
|
||||
QFileSystemEntry(qfilename, QFileSystemEntry::FromInternalPath()))
|
||||
.nativeFilePath();
|
||||
|
||||
// Find mask in native path
|
||||
phPos = filename.length();
|
||||
phLength = 0;
|
||||
while (phPos != 0) {
|
||||
--phPos;
|
||||
|
||||
if (filename[phPos] == Latin1Char('X')) {
|
||||
++phLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phLength >= 6) {
|
||||
++phPos;
|
||||
break;
|
||||
}
|
||||
|
||||
// start over
|
||||
phLength = 0;
|
||||
}
|
||||
|
||||
Q_ASSERT(phLength >= 6);
|
||||
path = filename;
|
||||
pos = phPos;
|
||||
length = phLength;
|
||||
}
|
||||
|
||||
// The following code is a heavily modified version, originally copyright:
|
||||
|
|
@ -120,12 +169,8 @@ struct NativeTemplate {
|
|||
Generates a unique file path from the template \a templ and returns it.
|
||||
The path in \c templ.path is modified.
|
||||
*/
|
||||
static QFileSystemEntry::NativePath generateNameFromTemplate(NativeTemplate &templ)
|
||||
QFileSystemEntry::NativePath QTemporaryFileName::generateNext()
|
||||
{
|
||||
QFileSystemEntry::NativePath &path = templ.path;
|
||||
qssize_t pos = templ.pos;
|
||||
qssize_t length = templ.length;
|
||||
|
||||
Q_ASSERT(length != 0);
|
||||
Q_ASSERT(pos < path.length());
|
||||
Q_ASSERT(length <= path.length() - pos);
|
||||
|
|
@ -159,6 +204,8 @@ static QFileSystemEntry::NativePath generateNameFromTemplate(NativeTemplate &tem
|
|||
return path;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_TEMPORARYFILE
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
|
|
@ -172,13 +219,13 @@ static QFileSystemEntry::NativePath generateNameFromTemplate(NativeTemplate &tem
|
|||
condition in \a error. In both cases, the string in \a templ will be
|
||||
changed and contain the generated path name.
|
||||
*/
|
||||
static bool createFileFromTemplate(NativeFileHandle &file, NativeTemplate &templ,
|
||||
static bool createFileFromTemplate(NativeFileHandle &file, QTemporaryFileName &templ,
|
||||
quint32 mode, QSystemError &error)
|
||||
{
|
||||
const int maxAttempts = 16;
|
||||
for (int attempt = 0; attempt < maxAttempts; ++attempt) {
|
||||
// Atomically create file and obtain handle
|
||||
const QFileSystemEntry::NativePath &path = generateNameFromTemplate(templ);
|
||||
const QFileSystemEntry::NativePath &path = templ.generateNext();
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
Q_UNUSED(mode);
|
||||
|
|
@ -261,62 +308,6 @@ void QTemporaryFileEngine::setFileName(const QString &file)
|
|||
QFSFileEngine::setFileName(file);
|
||||
}
|
||||
|
||||
static NativeTemplate makeNativeTemplate(QString qfilename)
|
||||
{
|
||||
// Ensure there is a placeholder mask
|
||||
uint phPos = qfilename.length();
|
||||
uint phLength = 0;
|
||||
|
||||
while (phPos != 0) {
|
||||
--phPos;
|
||||
|
||||
if (qfilename[phPos] == QLatin1Char('X')) {
|
||||
++phLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phLength >= 6
|
||||
|| qfilename[phPos] == QLatin1Char('/')) {
|
||||
++phPos;
|
||||
break;
|
||||
}
|
||||
|
||||
// start over
|
||||
phLength = 0;
|
||||
}
|
||||
|
||||
if (phLength < 6)
|
||||
qfilename.append(QLatin1String(".XXXXXX"));
|
||||
|
||||
// "Nativify" :-)
|
||||
QFileSystemEntry::NativePath filename = QFileSystemEngine::absoluteName(
|
||||
QFileSystemEntry(qfilename, QFileSystemEntry::FromInternalPath()))
|
||||
.nativeFilePath();
|
||||
|
||||
// Find mask in native path
|
||||
phPos = filename.length();
|
||||
phLength = 0;
|
||||
while (phPos != 0) {
|
||||
--phPos;
|
||||
|
||||
if (filename[phPos] == Latin1Char('X')) {
|
||||
++phLength;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phLength >= 6) {
|
||||
++phPos;
|
||||
break;
|
||||
}
|
||||
|
||||
// start over
|
||||
phLength = 0;
|
||||
}
|
||||
|
||||
Q_ASSERT(phLength >= 6);
|
||||
return {filename, phPos, phLength};
|
||||
}
|
||||
|
||||
bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
|
||||
{
|
||||
Q_D(QFSFileEngine);
|
||||
|
|
@ -327,8 +318,7 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
|
|||
if (!filePathIsTemplate)
|
||||
return QFSFileEngine::open(openMode);
|
||||
|
||||
QString qfilename = templateName;
|
||||
NativeTemplate templ = makeNativeTemplate(qfilename);
|
||||
QTemporaryFileName tfn(templateName);
|
||||
|
||||
QSystemError error;
|
||||
#if defined(Q_OS_WIN)
|
||||
|
|
@ -337,12 +327,12 @@ bool QTemporaryFileEngine::open(QIODevice::OpenMode openMode)
|
|||
NativeFileHandle &file = d->fd;
|
||||
#endif
|
||||
|
||||
if (!createFileFromTemplate(file, templ, fileMode, error)) {
|
||||
if (!createFileFromTemplate(file, tfn, fileMode, error)) {
|
||||
setError(QFile::OpenError, error.toString());
|
||||
return false;
|
||||
}
|
||||
|
||||
d->fileEntry = QFileSystemEntry(templ.path, QFileSystemEntry::FromNativePath());
|
||||
d->fileEntry = QFileSystemEntry(tfn.path, QFileSystemEntry::FromNativePath());
|
||||
|
||||
#if !defined(Q_OS_WIN) || defined(Q_OS_WINRT)
|
||||
d->closeFileHandle = true;
|
||||
|
|
@ -815,10 +805,10 @@ bool QTemporaryFile::open(OpenMode flags)
|
|||
return false;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_TEMPORARYFILE
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#ifndef QT_NO_QOBJECT
|
||||
#include "moc_qtemporaryfile.cpp"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -53,14 +53,25 @@
|
|||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#ifndef QT_NO_TEMPORARYFILE
|
||||
|
||||
#include "private/qfsfileengine_p.h"
|
||||
#include "private/qfilesystemengine_p.h"
|
||||
#include "private/qfile_p.h"
|
||||
#include "qtemporaryfile.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
struct QTemporaryFileName
|
||||
{
|
||||
QFileSystemEntry::NativePath path;
|
||||
qssize_t pos;
|
||||
qssize_t length;
|
||||
|
||||
QTemporaryFileName(const QString &templateName);
|
||||
QFileSystemEntry::NativePath generateNext();
|
||||
};
|
||||
|
||||
#ifndef QT_NO_TEMPORARYFILE
|
||||
|
||||
class QTemporaryFilePrivate : public QFilePrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QTemporaryFile)
|
||||
|
|
@ -120,9 +131,9 @@ public:
|
|||
bool filePathWasTemplate;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_TEMPORARYFILE
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif /* QTEMPORARYFILE_P_H */
|
||||
|
||||
|
|
|
|||
|
|
@ -148,25 +148,24 @@ void tst_QTemporaryDir::fileTemplate_data()
|
|||
{
|
||||
QTest::addColumn<QString>("constructorTemplate");
|
||||
QTest::addColumn<QString>("prefix");
|
||||
QTest::addColumn<QString>("suffix");
|
||||
|
||||
QTest::newRow("constructor default") << "" << "tst_qtemporarydir-";
|
||||
QTest::newRow("default") << "" << "tst_qtemporarydir-" << "";
|
||||
|
||||
QTest::newRow("constructor with xxx sufix") << "qt_XXXXXXxxx" << "qt_XXXXXXxxx";
|
||||
QTest::newRow("constructor with xXx sufix") << "qt_XXXXXXxXx" << "qt_XXXXXXxXx";
|
||||
QTest::newRow("constructor with no suffix") << "qt_XXXXXX" << "qt_";
|
||||
QTest::newRow("constructor with >6 X's, no suffix") << "qt_XXXXXXXXXX" << "qt_";
|
||||
// When more than 6 X are present at the end, linux and windows will only replace the last 6,
|
||||
// while Mac OS will actually replace all of them so we can only expect "qt_" (and check isValid).
|
||||
QTest::newRow("constructor with XXXX suffix") << "qt_XXXXXX_XXXX" << "qt_";
|
||||
QTest::newRow("constructor with XXXX prefix") << "qt_XXXX" << "qt_";
|
||||
QTest::newRow("constructor with XXXXX prefix") << "qt_XXXXX" << "qt_";
|
||||
QTest::newRow("xxx-suffix") << "qt_XXXXXXxxx" << "qt_" << "xxx";
|
||||
QTest::newRow("xXx-suffix") << "qt_XXXXXXxXx" << "qt_" << "xXx";
|
||||
QTest::newRow("no-suffix") << "qt_XXXXXX" << "qt_" << "";
|
||||
QTest::newRow("10X") << "qt_XXXXXXXXXX" << "qt_" << "";
|
||||
QTest::newRow("4Xsuffix") << "qt_XXXXXX_XXXX" << "qt_" << "_XXXX";
|
||||
QTest::newRow("4Xprefix") << "qt_XXXX" << "qt_XXXX" << "";
|
||||
QTest::newRow("5Xprefix") << "qt_XXXXX" << "qt_XXXXX" << "";
|
||||
if (canHandleUnicodeFileNames()) {
|
||||
// Test Umlauts (contained in Latin1)
|
||||
QString prefix = "qt_" + umlautTestText();
|
||||
QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix;
|
||||
// Test Chinese
|
||||
QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix << "";
|
||||
// test non-Latin1
|
||||
prefix = "qt_" + hanTestText();
|
||||
QTest::newRow("Chinese characters") << (prefix + "XXXXXX") << prefix;
|
||||
QTest::newRow("Chinese") << (prefix + "XXXXXX" + umlautTestText()) << prefix << umlautTestText();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,14 +173,17 @@ void tst_QTemporaryDir::fileTemplate()
|
|||
{
|
||||
QFETCH(QString, constructorTemplate);
|
||||
QFETCH(QString, prefix);
|
||||
QFETCH(QString, suffix);
|
||||
|
||||
QTemporaryDir tempDir(constructorTemplate);
|
||||
|
||||
QVERIFY(tempDir.isValid());
|
||||
|
||||
QString dirName = QDir(tempDir.path()).dirName();
|
||||
if (prefix.length())
|
||||
if (prefix.length()) {
|
||||
QCOMPARE(dirName.left(prefix.length()), prefix);
|
||||
QCOMPARE(dirName.right(suffix.length()), suffix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue