Fix QTemporaryDir to handle Unicode characters on Windows
For platforms not providing mkdtemp(), QTemporaryDir relied on an implementation of q_mkdtemp() operating on char *, converting back and forth using QFile::encodeName()/decodeName() when passing the name to QFileSystemEngine. This caused failures on Windows (which uses "System"/Latin1 encoding) for names containing characters outside the Latin1 space. Reimplement q_mkdtemp() to operate on QString, which avoids the conversions altogether and also enables the use of larger character spaces for the pattern. Add tests. Task-number: QTBUG-54810 Change-Id: Ie4323ad73b5beb8a1b8ab81425f73d03c626d58a Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
b2029e9ca6
commit
c14c149b51
|
|
@ -44,8 +44,12 @@
|
|||
#include "qcoreapplication.h"
|
||||
#endif
|
||||
|
||||
#if !defined(Q_OS_QNX) && !defined(Q_OS_WIN) &&!defined(Q_OS_ANDROID)
|
||||
# define USE_SYSTEM_MKDTEMP
|
||||
#endif
|
||||
|
||||
#include <stdlib.h> // mkdtemp
|
||||
#if defined(Q_OS_QNX) || defined(Q_OS_WIN) || defined(Q_OS_ANDROID)
|
||||
#ifndef USE_SYSTEM_MKDTEMP
|
||||
#include <private/qfilesystemengine_p.h>
|
||||
#endif
|
||||
|
||||
|
|
@ -91,8 +95,7 @@ static QString defaultTemplateName()
|
|||
return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String("-XXXXXX");
|
||||
}
|
||||
|
||||
#if defined(Q_OS_QNX ) || defined(Q_OS_WIN) || defined(Q_OS_ANDROID)
|
||||
|
||||
#ifndef USE_SYSTEM_MKDTEMP
|
||||
static int nextRand(int &v)
|
||||
{
|
||||
int r = v % 62;
|
||||
|
|
@ -102,30 +105,28 @@ static int nextRand(int &v)
|
|||
return r;
|
||||
}
|
||||
|
||||
QPair<QString, bool> q_mkdtemp(char *templateName)
|
||||
QPair<QString, bool> q_mkdtemp(QString templateName)
|
||||
{
|
||||
Q_ASSERT(templateName.endsWith(QLatin1String("XXXXXX")));
|
||||
|
||||
static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
const size_t length = strlen(templateName);
|
||||
const int length = templateName.size();
|
||||
|
||||
char *XXXXXX = templateName + length - 6;
|
||||
|
||||
Q_ASSERT((length >= 6u) && strncmp(XXXXXX, "XXXXXX", 6) == 0);
|
||||
QChar *XXXXXX = templateName.data() + length - 6;
|
||||
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
int v = qrand();
|
||||
|
||||
/* Fill in the random bits. */
|
||||
XXXXXX[0] = letters[nextRand(v)];
|
||||
XXXXXX[1] = letters[nextRand(v)];
|
||||
XXXXXX[2] = letters[nextRand(v)];
|
||||
XXXXXX[3] = letters[nextRand(v)];
|
||||
XXXXXX[4] = letters[nextRand(v)];
|
||||
XXXXXX[5] = letters[v % 62];
|
||||
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]);
|
||||
|
||||
QString templateNameStr = QFile::decodeName(templateName);
|
||||
|
||||
QFileSystemEntry fileSystemEntry(templateNameStr);
|
||||
QFileSystemEntry fileSystemEntry(templateName);
|
||||
if (QFileSystemEngine::createDirectory(fileSystemEntry, false)) {
|
||||
QSystemError error;
|
||||
QFileSystemEngine::setPermissions(fileSystemEntry,
|
||||
|
|
@ -134,10 +135,10 @@ QPair<QString, bool> q_mkdtemp(char *templateName)
|
|||
QFile::ExeOwner, error);
|
||||
if (error.error() != 0) {
|
||||
if (!QFileSystemEngine::removeDirectory(fileSystemEntry, false))
|
||||
qWarning() << "Unable to remove unused directory" << templateNameStr;
|
||||
qWarning() << "Unable to remove unused directory" << templateName;
|
||||
continue;
|
||||
}
|
||||
return qMakePair(QFile::decodeName(templateName), true);
|
||||
return qMakePair(templateName, true);
|
||||
}
|
||||
# ifdef Q_OS_WIN
|
||||
const int exists = ERROR_ALREADY_EXISTS;
|
||||
|
|
@ -152,7 +153,7 @@ QPair<QString, bool> q_mkdtemp(char *templateName)
|
|||
return qMakePair(qt_error_string(), false);
|
||||
}
|
||||
|
||||
#else // defined(Q_OS_QNX ) || defined(Q_OS_WIN) || defined(Q_OS_ANDROID)
|
||||
#else // !USE_SYSTEM_MKDTEMP
|
||||
|
||||
QPair<QString, bool> q_mkdtemp(char *templateName)
|
||||
{
|
||||
|
|
@ -160,14 +161,21 @@ QPair<QString, bool> q_mkdtemp(char *templateName)
|
|||
return qMakePair(ok ? QFile::decodeName(templateName) : qt_error_string(), ok);
|
||||
}
|
||||
|
||||
#endif
|
||||
#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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include <qfile.h>
|
||||
#include <qdir.h>
|
||||
#include <qset.h>
|
||||
#include <qtextcodec.h>
|
||||
#ifdef Q_OS_WIN
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
|
@ -113,6 +114,38 @@ void tst_QTemporaryDir::getSetCheck()
|
|||
QCOMPARE(true, obj1.autoRemove());
|
||||
}
|
||||
|
||||
static inline bool canHandleUnicodeFileNames()
|
||||
{
|
||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
|
||||
return true;
|
||||
#else
|
||||
// Check for UTF-8 by converting the Euro symbol (see tst_utf8)
|
||||
return QFile::encodeName(QString(QChar(0x20AC))) == QByteArrayLiteral("\342\202\254");
|
||||
#endif
|
||||
}
|
||||
|
||||
static QString hanTestText()
|
||||
{
|
||||
QString text;
|
||||
text += QChar(0x65B0);
|
||||
text += QChar(0x5E10);
|
||||
text += QChar(0x6237);
|
||||
return text;
|
||||
}
|
||||
|
||||
static QString umlautTestText()
|
||||
{
|
||||
QString text;
|
||||
text += QChar(0xc4);
|
||||
text += QChar(0xe4);
|
||||
text += QChar(0xd6);
|
||||
text += QChar(0xf6);
|
||||
text += QChar(0xdc);
|
||||
text += QChar(0xfc);
|
||||
text += QChar(0xdf);
|
||||
return text;
|
||||
}
|
||||
|
||||
void tst_QTemporaryDir::fileTemplate_data()
|
||||
{
|
||||
QTest::addColumn<QString>("constructorTemplate");
|
||||
|
|
@ -129,6 +162,14 @@ void tst_QTemporaryDir::fileTemplate_data()
|
|||
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_";
|
||||
if (canHandleUnicodeFileNames()) {
|
||||
// Test Umlauts (contained in Latin1)
|
||||
QString prefix = "qt_" + umlautTestText();
|
||||
QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix;
|
||||
// Test Chinese
|
||||
prefix = "qt_" + hanTestText();
|
||||
QTest::newRow("Chinese characters") << (prefix + "XXXXXX") << prefix;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTemporaryDir::fileTemplate()
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
#include <qfile.h>
|
||||
#include <qdir.h>
|
||||
#include <qset.h>
|
||||
#include <qtextcodec.h>
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
# include <windows.h>
|
||||
|
|
@ -145,6 +146,38 @@ void tst_QTemporaryFile::getSetCheck()
|
|||
QCOMPARE(true, obj1.autoRemove());
|
||||
}
|
||||
|
||||
static inline bool canHandleUnicodeFileNames()
|
||||
{
|
||||
#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
|
||||
return true;
|
||||
#else
|
||||
// Check for UTF-8 by converting the Euro symbol (see tst_utf8)
|
||||
return QFile::encodeName(QString(QChar(0x20AC))) == QByteArrayLiteral("\342\202\254");
|
||||
#endif
|
||||
}
|
||||
|
||||
static QString hanTestText()
|
||||
{
|
||||
QString text;
|
||||
text += QChar(0x65B0);
|
||||
text += QChar(0x5E10);
|
||||
text += QChar(0x6237);
|
||||
return text;
|
||||
}
|
||||
|
||||
static QString umlautTestText()
|
||||
{
|
||||
QString text;
|
||||
text += QChar(0xc4);
|
||||
text += QChar(0xe4);
|
||||
text += QChar(0xd6);
|
||||
text += QChar(0xf6);
|
||||
text += QChar(0xdc);
|
||||
text += QChar(0xfc);
|
||||
text += QChar(0xdf);
|
||||
return text;
|
||||
}
|
||||
|
||||
void tst_QTemporaryFile::fileTemplate_data()
|
||||
{
|
||||
QTest::addColumn<QString>("constructorTemplate");
|
||||
|
|
@ -171,6 +204,14 @@ void tst_QTemporaryFile::fileTemplate_data()
|
|||
QTest::newRow("set template, with xxx") << "" << "qt_" << ".xxx" << "qt_XXXXXX.xxx";
|
||||
QTest::newRow("set template, with >6 X's") << "" << "qt_" << ".xxx" << "qt_XXXXXXXXXXXXXX.xxx";
|
||||
QTest::newRow("set template, with >6 X's, no suffix") << "" << "qt_" << "" << "qt_XXXXXXXXXXXXXX";
|
||||
if (canHandleUnicodeFileNames()) {
|
||||
// Test Umlauts (contained in Latin1)
|
||||
QString prefix = "qt_" + umlautTestText();
|
||||
QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix << QString() << QString();
|
||||
// Test Chinese
|
||||
prefix = "qt_" + hanTestText();
|
||||
QTest::newRow("Chinese characters") << (prefix + "XXXXXX") << prefix << QString() << QString();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTemporaryFile::fileTemplate()
|
||||
|
|
|
|||
Loading…
Reference in New Issue