summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2016-07-19 10:18:39 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2016-07-20 07:31:18 +0000
commitc14c149b51a1c7bf01e4e039f6e8cf1819e37ca6 (patch)
treeeefa8c93d9d50d5a02dfa71d26bf05ae4066be68
parentb2029e9ca6c1645e85cbada1b09ba63fd1ee31ed (diff)
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>
-rw-r--r--src/corelib/io/qtemporarydir.cpp52
-rw-r--r--tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp41
-rw-r--r--tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp41
3 files changed, 112 insertions, 22 deletions
diff --git a/src/corelib/io/qtemporarydir.cpp b/src/corelib/io/qtemporarydir.cpp
index 71436c6497..d869318e35 100644
--- a/src/corelib/io/qtemporarydir.cpp
+++ b/src/corelib/io/qtemporarydir.cpp
@@ -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)
{
- static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ Q_ASSERT(templateName.endsWith(QLatin1String("XXXXXX")));
- const size_t length = strlen(templateName);
+ static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- char *XXXXXX = templateName + length - 6;
+ const int length = templateName.size();
- 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];
-
- QString templateNameStr = QFile::decodeName(templateName);
-
- QFileSystemEntry fileSystemEntry(templateNameStr);
+ 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);
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;
}
diff --git a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
index 6e03d8360e..67a39f21ca 100644
--- a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
+++ b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp
@@ -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()
diff --git a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
index 7b06355990..7fdc8fd44c 100644
--- a/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
+++ b/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp
@@ -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()