diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml
index 64cc295e4..1f88a4c02 100644
--- a/.github/workflows/actions.yml
+++ b/.github/workflows/actions.yml
@@ -364,7 +364,7 @@ jobs:
vs-version: '[17,18)' # 17.xx
msbuild-architecture: x64
- name: Install Qt
- uses: jurplel/install-qt-action@v4
+ uses: jurplel/install-qt-action@v4.1.1
with:
version: 6.8.0
host: windows
@@ -414,7 +414,7 @@ jobs:
steps:
- uses: actions/checkout@main
- name: Install Qt
- uses: jurplel/install-qt-action@v4
+ uses: jurplel/install-qt-action@v4.1.1
with:
version: 6.8.0
host: windows
@@ -466,7 +466,7 @@ jobs:
steps:
- uses: actions/checkout@main
- name: Install Qt
- uses: jurplel/install-qt-action@v4
+ uses: jurplel/install-qt-action@v4.1.1
with:
version: 6.5.2
host: windows
diff --git a/configure.bat b/configure.bat
index 61503ad6c..f8abd05d2 100644
--- a/configure.bat
+++ b/configure.bat
@@ -1,7 +1,7 @@
@echo off
@setlocal
-set VERSION=2.10.1
+set VERSION=2.11.0
set TFDIR=C:\TreeFrog\%VERSION%
set MONBOC_VERSION=1.26.2
set LZ4_VERSION=1.9.4
diff --git a/defaults/development.ini b/defaults/development.ini
index ddb5f0939..a90d60322 100644
--- a/defaults/development.ini
+++ b/defaults/development.ini
@@ -7,8 +7,8 @@
## Template system section
##
-# Specify the template system of view, ERB or Otama.
-TemplateSystem=ERB
+# Template system of view.
+TemplateSystem=$TemplateSystem$
##
diff --git a/installer/create_installer.bat b/installer/create_installer.bat
index 801f1fe20..a06592ebd 100644
--- a/installer/create_installer.bat
+++ b/installer/create_installer.bat
@@ -7,7 +7,7 @@
:: 10行目、28行目、39行目を編集
-set VERSION=2.10.1
+set VERSION=2.11.0
set QTBASE=C:\Qt
set TFDIR=C:\TreeFrog\%VERSION%
diff --git a/installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp b/installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp
index 5e9021252..1010c389c 100644
--- a/installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp
+++ b/installer/treefrog-setup/treefrog-setup/AssemblyInfo.cpp
@@ -31,7 +31,7 @@ using namespace System::Security::Permissions;
// すべての値を指定するか、下のように '*' を使ってリビジョンおよびビルド番号を
// 既定値にすることができます:
-[assembly:AssemblyVersionAttribute("2.10.1")];
+[assembly:AssemblyVersionAttribute("2.11.0")];
[assembly:ComVisible(false)];
diff --git a/src/corelib.pro b/src/corelib.pro
index 076b4dc32..a0b93c2fc 100644
--- a/src/corelib.pro
+++ b/src/corelib.pro
@@ -412,6 +412,7 @@ linux-* {
SOURCES += tthreadapplicationserver_linux.cpp
SOURCES += tredisdriver_linux.cpp
SOURCES += tmemcacheddriver_linux.cpp
+ SOURCES += tsharedmemory_linux.cpp
}
# For Mac
@@ -420,6 +421,7 @@ macx {
SOURCES += tthreadapplicationserver_qt.cpp
SOURCES += tredisdriver_qt.cpp
SOURCES += tmemcacheddriver_qt.cpp
+ SOURCES += tsharedmemory_macx.cpp
}
# For UNIX
diff --git a/src/tabstractcontroller.h b/src/tabstractcontroller.h
index 1107df6ef..19749bc88 100644
--- a/src/tabstractcontroller.h
+++ b/src/tabstractcontroller.h
@@ -23,6 +23,10 @@ class T_CORE_EXPORT TAbstractController : public QObject {
virtual const TSession &session() const;
virtual QString getRenderingData(const QString &templateName, const QVariantMap &vars = QVariantMap());
virtual QByteArray authenticityToken() const { return QByteArray(); }
+ virtual QVariantMap flashVariants() const { return QVariantMap(); }
+ virtual QVariant flashVariant(const QString &) const { return QVariant(); }
+ virtual QJsonObject flashVariantsJson() const { return QJsonObject(); }
+ virtual QJsonObject flashVariantJson(const QString &) const { return QJsonObject(); }
virtual void setFlash(const QString &name, const QVariant &value);
void exportVariant(const QString &name, const QVariant &value, bool overwrite = true);
virtual bool isUserLoggedIn() const;
diff --git a/src/tactioncontext.cpp b/src/tactioncontext.cpp
index 642c13299..6d2268b4c 100644
--- a/src/tactioncontext.cpp
+++ b/src/tactioncontext.cpp
@@ -155,10 +155,8 @@ void TActionContext::execute(THttpRequest &request)
tSystemDebug("Re-generate session ID: {}", (const char*)_currController->session().sessionId.data());
}
- if (EnableCsrfProtectionModuleFlag && _currController->csrfProtectionEnabled()) {
- // Sets CSRF protection information
- TActionController::setCsrfProtectionInto(_currController->session());
- }
+ // Sets CSRF protection information
+ TActionController::setCsrfProtectionInto(_currController->session());
}
// Database Transaction
diff --git a/src/tactioncontroller.cpp b/src/tactioncontroller.cpp
index bb963273f..a837c07d2 100644
--- a/src/tactioncontroller.cpp
+++ b/src/tactioncontroller.cpp
@@ -31,7 +31,8 @@
#include
#include
-const QString FLASH_VARS_SESSION_KEY("_flashVariants");
+const QString QUEUED_FLASH_SESSION_KEY("_flashVariants");
+const QString FLASH_VARS_SESSION_KEY("_activeFlash");
const QString LOGIN_USER_NAME_KEY("_loginUserName");
const QByteArray DEFAULT_CONTENT_TYPE("text/html");
@@ -207,7 +208,11 @@ void TActionController::setCsrfProtectionInto(TSession &session)
{
if (TSessionManager::instance().storeType() == QLatin1String("cookie")) {
QString key = TSessionManager::instance().csrfProtectionKey();
- session.insert(key, TSessionManager::instance().generateId()); // it's just a random value
+ QByteArray val = session.value(key).toByteArray();
+
+ if (val.isEmpty()) {
+ session.insert(key, TSessionManager::instance().generateId()); // it's just a random value
+ }
}
}
@@ -248,6 +253,7 @@ QString TActionController::loginUserNameKey()
bool TActionController::verifyRequest(const THttpRequest &request) const
{
if (!csrfProtectionEnabled()) {
+ tSystemWarn("Skipped verifying authenticity token : {}", request.header().path().data());
return true;
}
@@ -263,7 +269,11 @@ bool TActionController::verifyRequest(const THttpRequest &request) const
}
tSystemDebug("postAuthToken: {}", (const char*)postAuthToken.data());
- return Tf::strcmp(postAuthToken, authenticityToken());
+ bool res = Tf::strcmp(postAuthToken, authenticityToken());
+ if (res) {
+ tSystemDebug("Verified authenticity token : {}", request.header().path().data());
+ }
+ return res;
}
/*!
@@ -595,7 +605,7 @@ void TActionController::redirect(const QUrl &url, int statusCode)
// Enable flash-variants
QVariant var;
var.setValue(_flashVars);
- _sessionStore.insert(FLASH_VARS_SESSION_KEY, var);
+ _sessionStore.insert(QUEUED_FLASH_SESSION_KEY, var);
}
/*!
@@ -655,12 +665,39 @@ bool TActionController::sendData(const QByteArray &data, const QByteArray &conte
*/
void TActionController::exportAllFlashVariants()
{
- QVariant var = _sessionStore.take(FLASH_VARS_SESSION_KEY);
+ _sessionStore.remove(FLASH_VARS_SESSION_KEY);
+
+ QVariant var = _sessionStore.take(QUEUED_FLASH_SESSION_KEY);
if (!var.isNull()) {
exportVariants(var.toMap());
+ _sessionStore.insert(FLASH_VARS_SESSION_KEY, var);
}
}
+
+QVariantMap TActionController::flashVariants() const
+{
+ return _sessionStore.value(FLASH_VARS_SESSION_KEY).toMap();
+}
+
+
+QVariant TActionController::flashVariant(const QString &key) const
+{
+ return _sessionStore.value(FLASH_VARS_SESSION_KEY).toMap().value(key);
+}
+
+
+QJsonObject TActionController::flashVariantsJson() const
+{
+ return QJsonObject::fromVariantMap(flashVariants());
+}
+
+
+QJsonObject TActionController::flashVariantJson(const QString &key) const
+{
+ return QJsonObject::fromVariantMap(flashVariant(key).toMap());
+}
+
/*!
Validates the access of the user \a user. Returns true if the user
access is allowed by rule; otherwise returns false.
diff --git a/src/tactioncontroller.h b/src/tactioncontroller.h
index 90c096cda..226409cf2 100644
--- a/src/tactioncontroller.h
+++ b/src/tactioncontroller.h
@@ -37,7 +37,11 @@ class T_CORE_EXPORT TActionController : public TAbstractController, public TActi
virtual QStringList exceptionActionsOfCsrfProtection() const { return QStringList(); }
virtual bool transactionEnabled() const { return true; }
QByteArray authenticityToken() const override;
- QString flash(const QString &name) const;
+ QVariantMap flashVariants() const override;
+ QVariant flashVariant(const QString &key) const override;
+ QJsonObject flashVariantsJson() const override;
+ QJsonObject flashVariantJson(const QString &key) const override;
+ //QString flash(const QString &name) const;
QHostAddress clientAddress() const;
virtual bool isUserLoggedIn() const override;
virtual QString identityKeyOfLoginUser() const;
@@ -199,10 +203,10 @@ inline void TActionController::setStatusCode(int code)
_statCode = code;
}
-inline QString TActionController::flash(const QString &name) const
-{
- return _flashVars.value(name).toString();
-}
+// inline QString TActionController::flash(const QString &name) const
+// {
+// return _flashVars.value(name).toString();
+// }
inline QByteArray TActionController::contentType() const
{
diff --git a/src/tactionview.cpp b/src/tactionview.cpp
index 8430b1d5a..f20e155df 100644
--- a/src/tactionview.cpp
+++ b/src/tactionview.cpp
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2019, AOYAMA Kazuharu
+/* Copyright (c) 2010-2025, AOYAMA Kazuharu
* All rights reserved.
*
* This software may be used and distributed according to the terms of
@@ -72,68 +72,122 @@ QString TActionView::authenticityToken() const
}
/*!
- Outputs the string of the HTML attribute \a attr to a view
- template.
+ Returns flash variants;
*/
-QString TActionView::echo(const THtmlAttribute &attr)
+QVariantMap TActionView::flashVariants() const
{
- responsebody += attr.toString().trimmed();
- return QString();
+ static QVariantMap dummy;
+ return (actionController) ? actionController->flashVariants() : dummy;
}
+/*!
+ \fn QString TActionView::echo(const THtmlAttribute &attr)
+ Outputs the string of the HTML attribute \a attr to a view
+ template.
+*/
+
/*!
\fn QString TActionView::echo(const QVariant &var)
Outputs the variant variable \a var to a view template.
*/
-QString TActionView::echo(const QVariant &var)
-{
- if (var.userType() == QMetaType::QUrl) {
- responsebody += var.toUrl().toString(QUrl::FullyEncoded);
- } else {
- responsebody += var.toString();
- }
- return QString();
-}
/*!
+ \fn QString TActionView::echo(const QVariantMap &map)
Outputs the variantmap variable \a map to a view template.
*/
-QString TActionView::echo(const QVariantMap &map)
-{
- responsebody += QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact);
- return QString();
-}
/*!
+ \fn QString TActionView::eh(const THtmlAttribute &attr)
Outputs a escaped string of the HTML attribute \a attr to
a view template.
*/
-QString TActionView::eh(const THtmlAttribute &attr)
-{
- return echo(THttpUtility::htmlEscape(attr.toString().trimmed()));
-}
/*!
\fn QString TActionView::eh(const QVariant &var)
Outputs a escaped string of the variant variable \a var
to a view template.
*/
-QString TActionView::eh(const QVariant &var)
+
+/*!
+ \fn QString TActionView::eh(const QVariantMap &map)
+ Outputs a escaped string of the variantmap variable \a map
+ to a view template.
+*/
+
+
+QString TActionView::fromValue(int n, int base)
+{
+ return QString::number(n, base);
+}
+
+
+QString TActionView::fromValue(long n, int base)
+{
+ return QString::number(n, base);
+}
+
+
+QString TActionView::fromValue(ulong n, int base)
+{
+ return QString::number(n, base);
+}
+
+
+QString TActionView::fromValue(qlonglong n, int base)
+{
+ return QString::number(n, base);
+}
+
+
+QString TActionView::fromValue(qulonglong n, int base)
+{
+ return QString::number(n, base);
+}
+
+
+QString TActionView::fromValue(double d, char format, int precision)
+{
+ return QString::number(d, format, precision);
+}
+
+
+QString TActionView::fromValue(const QJsonObject &object)
+{
+ return QJsonDocument(object).toJson(QJsonDocument::Compact);
+}
+
+
+QString TActionView::fromValue(const QJsonArray &array)
+{
+ return QJsonDocument(array).toJson(QJsonDocument::Compact);
+}
+
+
+QString TActionView::fromValue(const QJsonDocument &doc)
+{
+ return doc.toJson(QJsonDocument::Compact);
+}
+
+
+QString TActionView::fromValue(const THtmlAttribute &attr)
+{
+ return attr.toString().trimmed();
+}
+
+
+QString TActionView::fromValue(const QVariant &var)
{
if (var.userType() == QMetaType::QUrl) {
- return echo(var.toUrl().toString(QUrl::FullyEncoded));
+ return var.toUrl().toString(QUrl::FullyEncoded);
} else {
- return echo(THttpUtility::htmlEscape(var.toString()));
+ return var.toString();
}
}
-/*!
- Outputs a escaped string of the variantmap variable \a map
- to a view template.
-*/
-QString TActionView::eh(const QVariantMap &map)
+
+QString TActionView::fromValue(const QVariantMap &map)
{
- return echo(THttpUtility::htmlEscape(QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact)));
+ return QJsonDocument::fromVariant(map).toJson(QJsonDocument::Compact);
}
/*!
diff --git a/src/tactionview.h b/src/tactionview.h
index 7886f53b5..0703b8453 100644
--- a/src/tactionview.h
+++ b/src/tactionview.h
@@ -23,6 +23,7 @@ class T_CORE_EXPORT TActionView : public QObject, public TActionHelper, public T
QVariant variant(const QString &name) const;
bool hasVariant(const QString &name) const;
const QVariantMap &allVariants() const;
+ QVariantMap flashVariants() const;
const TAbstractController *controller() const override;
const THttpRequest &httpRequest() const;
void reset();
@@ -62,6 +63,22 @@ class T_CORE_EXPORT TActionView : public QObject, public TActionHelper, public T
QString responsebody;
+ static inline QString fromValue(const QString &str) { return str; }
+ static inline QString fromValue(const char *str) { return QString(str); } // using codecForCStrings()
+ static inline QString fromValue(const QByteArray &str) { return QString(str); } // using codecForCStrings()
+ static QString fromValue(int n, int base = 10);
+ static QString fromValue(long n, int base = 10);
+ static QString fromValue(ulong n, int base = 10);
+ static QString fromValue(qlonglong n, int base = 10);
+ static QString fromValue(qulonglong n, int base = 10);
+ static QString fromValue(double d, char format = 'g', int precision = 6);
+ static QString fromValue(const QJsonObject &object);
+ static QString fromValue(const QJsonArray &array);
+ static QString fromValue(const QJsonDocument &doc);
+ static QString fromValue(const THtmlAttribute &attr);
+ static QString fromValue(const QVariant &var);
+ static QString fromValue(const QVariantMap &map);
+
private:
T_DISABLE_COPY(TActionView)
T_DISABLE_MOVE(TActionView)
@@ -113,66 +130,68 @@ inline const QVariantMap &TActionView::allVariants() const
inline QString TActionView::echo(const QString &str)
{
- responsebody += str;
+ responsebody += fromValue(str);
return QString();
}
inline QString TActionView::echo(const char *str)
{
- responsebody += QString(str); // using codecForCStrings()
+ responsebody += fromValue(str);
return QString();
}
inline QString TActionView::echo(const QByteArray &str)
{
- responsebody += QString(str); // using codecForCStrings()
+ responsebody += fromValue(str);
return QString();
}
inline QString TActionView::echo(int n, int base)
{
- responsebody += QString::number(n, base);
+ responsebody += fromValue(n, base);
return QString();
}
inline QString TActionView::echo(long n, int base)
{
- responsebody += QString::number(n, base);
+ responsebody += fromValue(n, base);
return QString();
}
inline QString TActionView::echo(ulong n, int base)
{
- responsebody += QString::number(n, base);
+ responsebody += fromValue(n, base);
return QString();
}
inline QString TActionView::echo(qlonglong n, int base)
{
- responsebody += QString::number(n, base);
+ responsebody += fromValue(n, base);
return QString();
}
inline QString TActionView::echo(qulonglong n, int base)
{
- responsebody += QString::number(n, base);
+ responsebody += fromValue(n, base);
return QString();
}
inline QString TActionView::echo(double d, char format, int precision)
{
- responsebody += QString::number(d, format, precision);
+ responsebody += fromValue(d, format, precision);
return QString();
}
inline QString TActionView::echo(const QJsonObject &object)
{
- return echo(QJsonDocument(object));
+ responsebody += fromValue(object);
+ return QString();
}
inline QString TActionView::echo(const QJsonArray &array)
{
- return echo(QJsonDocument(array));
+ responsebody += fromValue(array);
+ return QString();
}
inline QString TActionView::echo(const QJsonDocument &doc)
@@ -181,64 +200,97 @@ inline QString TActionView::echo(const QJsonDocument &doc)
return QString();
}
+inline QString TActionView::echo(const THtmlAttribute &attr)
+{
+ responsebody += fromValue(attr);
+ return QString();
+}
+
+inline QString TActionView::echo(const QVariant &var)
+{
+ responsebody += fromValue(var);
+ return QString();
+}
+
+inline QString TActionView::echo(const QVariantMap &map)
+{
+ responsebody += fromValue(map);
+ return QString();
+}
+
inline QString TActionView::eh(const QString &str)
{
- return echo(THttpUtility::htmlEscape(str));
+ return echo(THttpUtility::htmlEscape(fromValue(str)));
}
inline QString TActionView::eh(const char *str)
{
- return echo(THttpUtility::htmlEscape(str));
+ return echo(THttpUtility::htmlEscape(fromValue(str)));
}
inline QString TActionView::eh(const QByteArray &str)
{
- return echo(THttpUtility::htmlEscape(str));
+ return echo(THttpUtility::htmlEscape(fromValue(str)));
}
inline QString TActionView::eh(int n, int base)
{
- return echo(THttpUtility::htmlEscape(QString::number(n, base)));
+ return echo(THttpUtility::htmlEscape(fromValue(n, base)));
}
inline QString TActionView::eh(long n, int base)
{
- return echo(THttpUtility::htmlEscape(QString::number(n, base)));
+ return echo(THttpUtility::htmlEscape(fromValue(n, base)));
}
inline QString TActionView::eh(ulong n, int base)
{
- return echo(THttpUtility::htmlEscape(QString::number(n, base)));
+ return echo(THttpUtility::htmlEscape(fromValue(n, base)));
}
inline QString TActionView::eh(qlonglong n, int base)
{
- return echo(THttpUtility::htmlEscape(QString::number(n, base)));
+ return echo(THttpUtility::htmlEscape(fromValue(n, base)));
}
inline QString TActionView::eh(qulonglong n, int base)
{
- return echo(THttpUtility::htmlEscape(QString::number(n, base)));
+ return echo(THttpUtility::htmlEscape(fromValue(n, base)));
}
inline QString TActionView::eh(double d, char format, int precision)
{
- return echo(THttpUtility::htmlEscape(QString::number(d, format, precision)));
+ return echo(THttpUtility::htmlEscape(fromValue(d, format, precision)));
}
inline QString TActionView::eh(const QJsonObject &object)
{
- return eh(QJsonDocument(object));
+ return echo(THttpUtility::htmlEscape(fromValue(object)));
}
inline QString TActionView::eh(const QJsonArray &array)
{
- return eh(QJsonDocument(array));
+ return echo(THttpUtility::htmlEscape(fromValue(array)));
}
inline QString TActionView::eh(const QJsonDocument &doc)
{
- return echo(THttpUtility::htmlEscape(doc.toJson(QJsonDocument::Compact)));
+ return echo(THttpUtility::htmlEscape(fromValue(doc)));
+}
+
+inline QString TActionView::eh(const THtmlAttribute &attr)
+{
+ return echo(THttpUtility::htmlEscape(fromValue(attr)));
+}
+
+inline QString TActionView::eh(const QVariant &var)
+{
+ return echo(THttpUtility::htmlEscape(fromValue(var)));
+}
+
+inline QString TActionView::eh(const QVariantMap &map)
+{
+ return echo(THttpUtility::htmlEscape(fromValue(map)));
}
inline void TActionView::setController(TAbstractController *controller)
diff --git a/src/test/mailmessage/main.cpp b/src/test/mailmessage/main.cpp
index 200de6b18..742727c66 100644
--- a/src/test/mailmessage/main.cpp
+++ b/src/test/mailmessage/main.cpp
@@ -154,18 +154,31 @@ void TestMailMessage::dateTime_data()
QTest::addColumn("result");
// Timezone
+#if QT_VERSION >= 0x060700
+ uint utc = QDateTime(QDate(2000,1,1), QTime(0,0,0), QTimeZone::UTC).toSecsSinceEpoch();
+ uint local = QDateTime(QDate(2000,1,1), QTime(0,0,0), QTimeZone::LocalTime).toSecsSinceEpoch();
+#else
uint utc = QDateTime(QDate(2000,1,1), QTime(0,0,0), Qt::UTC).toSecsSinceEpoch();
uint local = QDateTime(QDate(2000,1,1), QTime(0,0,0), Qt::LocalTime).toSecsSinceEpoch();
+#endif
int offset = (utc - local) / 60;
QString offsetStr = QString("%1%2%3")
.arg(offset > 0 ? '+' : '-')
.arg(qAbs(offset) / 60, 2, 10, QLatin1Char('0'))
.arg(qAbs(offset) % 60, 2, 10, QLatin1Char('0'));
+#if QT_VERSION >= 0x060700
+ QTest::newRow("1") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::LocalTime) << "Mon, 28 Mar 2011 12:11:04 " + offsetStr;
+ QTest::newRow("2") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::LocalTime) << "Mon, 31 Mar 2014 01:00:00 " + offsetStr;
+ QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), QTimeZone::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000";
+ QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), QTimeZone::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000";
+#else
QTest::newRow("1") << QDateTime(QDate(2011,3,28), QTime(12,11,04), Qt::LocalTime) << "Mon, 28 Mar 2011 12:11:04 " + offsetStr;
QTest::newRow("2") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), Qt::LocalTime) << "Mon, 31 Mar 2014 01:00:00 " + offsetStr;
- QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), Qt::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000";
- QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), Qt::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000";
+ QTest::newRow("3") << QDateTime(QDate(2011,3,28), QTime(12,11,04), Qt::UTC) << "Mon, 28 Mar 2011 12:11:04 +0000";
+ QTest::newRow("4") << QDateTime(QDate(2014,3,31), QTime( 1, 0, 0), Qt::UTC) << "Mon, 31 Mar 2014 01:00:00 +0000";
+
+#endif
}
void TestMailMessage::dateTime()
diff --git a/src/test/sharedmemory/sharedmemory b/src/test/sharedmemory/sharedmemory
new file mode 100755
index 000000000..a1bc2e4fc
Binary files /dev/null and b/src/test/sharedmemory/sharedmemory differ
diff --git a/src/test/sharedmemory/sharedmemory.cpp b/src/test/sharedmemory/sharedmemory.cpp
new file mode 100644
index 000000000..d8ef80391
--- /dev/null
+++ b/src/test/sharedmemory/sharedmemory.cpp
@@ -0,0 +1,119 @@
+#include
+#include "tsharedmemory.h"
+#include "tglobal.h"
+#include
+
+
+namespace {
+TSharedMemory sharedMomory("testshared.shm");
+}
+
+
+class TestSharedMemory : public QObject
+{
+ Q_OBJECT
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+
+ void attach();
+ void test1();
+ void test2_data();
+ void test2();
+};
+
+
+static QByteArray randomString(int length)
+{
+ constexpr auto ch = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-+/^=_[]@:;!#$%()~? \t\n";
+ QByteArray ret;
+ int max = (int)strlen(ch) - 1;
+
+ for (int i = 0; i < length; ++i) {
+ ret += ch[Tf::random(max)];
+ }
+ return ret;
+}
+
+void TestSharedMemory::initTestCase()
+{
+ sharedMomory.unlink();
+ sharedMomory.create(100 * 1024 * 1024);
+}
+
+void TestSharedMemory::cleanupTestCase()
+{
+ sharedMomory.unlink();
+}
+
+void TestSharedMemory::attach()
+{
+ bool res = sharedMomory.attach();
+ Q_ASSERT(res);
+ Tf::msleep(1000);
+ res = sharedMomory.detach();
+ Q_ASSERT(res);
+ Tf::msleep(1000);
+ res = sharedMomory.attach();
+ Q_ASSERT(res);
+ res = sharedMomory.detach();
+ Q_ASSERT(res);
+}
+
+void TestSharedMemory::test1()
+{
+ sharedMomory.attach();
+ QVERIFY(sharedMomory.size() == 100 * 1024 * 1024);
+
+ // Lock and unlock
+ auto ptr = sharedMomory.data();
+ QVERIFY(ptr != nullptr);
+ bool res = sharedMomory.lockForRead();
+ Q_ASSERT(res);
+ res = sharedMomory.lockForWrite();
+ Q_ASSERT(!res);
+ res = sharedMomory.unlock();
+ Q_ASSERT(res);
+ res = sharedMomory.lockForWrite();
+ Q_ASSERT(res);
+ res = sharedMomory.unlock();
+ Q_ASSERT(res);
+ QVERIFY(ptr == sharedMomory.data());
+}
+
+void TestSharedMemory::test2_data()
+{
+ QTest::addColumn("string");
+
+ QTest::newRow("1") << QUuid::createUuid().toByteArray();
+ QTest::newRow("2") << QUuid::createUuid().toByteArray();
+ QTest::newRow("3") << randomString(128);
+ QTest::newRow("4") << randomString(256);
+ QTest::newRow("5") << randomString(1024);
+}
+
+void TestSharedMemory::test2()
+{
+ QFETCH(QByteArray, string);
+
+ bool res = sharedMomory.attach();
+ Q_ASSERT(res);
+ auto ptr = sharedMomory.data();
+ res = sharedMomory.lockForWrite();
+ Q_ASSERT(res);
+ std::memcpy(ptr, string.data(), string.length());
+ sharedMomory.unlock();
+ res = sharedMomory.detach();
+ Q_ASSERT(res);
+
+ res = sharedMomory.attach();
+ Q_ASSERT(res);
+ res = sharedMomory.lockForRead();
+ Q_ASSERT(res);
+ int cmp = strncmp((char *)sharedMomory.data(), string.data(), string.length());
+ Q_ASSERT(cmp == 0);
+ sharedMomory.unlock();
+}
+
+TF_TEST_MAIN(TestSharedMemory)
+#include "sharedmemory.moc"
diff --git a/src/test/sharedmemory/sharedmemory.pro b/src/test/sharedmemory/sharedmemory.pro
new file mode 100644
index 000000000..b644dfd48
--- /dev/null
+++ b/src/test/sharedmemory/sharedmemory.pro
@@ -0,0 +1,3 @@
+include(../test.pri)
+TARGET = sharedmemory
+SOURCES = sharedmemory.cpp
diff --git a/src/test/sharedmemoryhash/sharedmemoryhash.cpp b/src/test/sharedmemoryhash/sharedmemoryhash.cpp
index 1fc146df3..c8b5837cd 100644
--- a/src/test/sharedmemoryhash/sharedmemoryhash.cpp
+++ b/src/test/sharedmemoryhash/sharedmemoryhash.cpp
@@ -196,7 +196,7 @@ void TestSharedMemoryHash::testAlloc4()
smhash.set(key, "hoge", seconds);
QVERIFY(!smhash.get(key).isEmpty()); // not empty
qDebug() << "smhash.get(" << key << ") =" << smhash.get(key);
- Tf::msleep(seconds * 1000 + 1);
+ Tf::msleep(seconds * 1000 + 20);
auto val = smhash.get(key);
QCOMPARE(val, QByteArray()); // timeout, empty
}
diff --git a/src/test/test.pro b/src/test/test.pro
index 992efc95a..a481a0f37 100644
--- a/src/test/test.pro
+++ b/src/test/test.pro
@@ -5,7 +5,7 @@ SUBDIRS += mailmessage multipartformdata smtpmailer viewhelper paginator
SUBDIRS += fieldnametovariablename rand urlrouter urlrouter2
SUBDIRS += buildtest stack queue forlist
SUBDIRS += jscontext compression sqlitedb url malloc
-SUBDIRS += sharedmemoryhash sharedmemorymutex
+SUBDIRS += sharedmemory sharedmemoryhash sharedmemorymutex
unix {
SUBDIRS += redis memcached
}
diff --git a/src/test/testall.sh b/src/test/testall.sh
index 778e42d70..fa2fce840 100755
--- a/src/test/testall.sh
+++ b/src/test/testall.sh
@@ -8,11 +8,12 @@ cd $WORKDIR
for e in `ls -d *`; do
if [ -f "$e/Makefile" ]; then
- make -C $e clean
+ make -k -C $e clean
fi
done
-[ -f Makefile ] && make distclean
+[ -f Makefile ] && make -k distclean
+rm -f Makefile
qmake -r
make -j8
diff --git a/src/test/viewhelper/viewhelper.cpp b/src/test/viewhelper/viewhelper.cpp
index 9c034a0e9..e615a37f7 100644
--- a/src/test/viewhelper/viewhelper.cpp
+++ b/src/test/viewhelper/viewhelper.cpp
@@ -153,7 +153,7 @@ void TestViewHelper::stylesheetTag()
attr.append("onclick", "return 0;");
attr.append("style", "none");
QString actual = view.styleSheetTag("hoge.png", attr);
- QString result = "";
+ QString result = "";
QCOMPARE(actual, result);
}
diff --git a/src/tglobal.h b/src/tglobal.h
index ddbea826f..1dc8c010d 100644
--- a/src/tglobal.h
+++ b/src/tglobal.h
@@ -1,7 +1,7 @@
#pragma once
-constexpr auto TF_VERSION_STR = "2.10.1";
-constexpr auto TF_VERSION_NUMBER = 0x020a01;
-constexpr auto TF_SRC_REVISION = 3001;
+constexpr auto TF_VERSION_STR = "2.11.0";
+constexpr auto TF_VERSION_NUMBER = 0x020b00;
+constexpr auto TF_SRC_REVISION = 3013;
#include
#include
diff --git a/src/tsharedmemory.h b/src/tsharedmemory.h
index c5545dab4..2cb4002f4 100644
--- a/src/tsharedmemory.h
+++ b/src/tsharedmemory.h
@@ -3,6 +3,10 @@
#ifdef Q_OS_WIN
#include
#endif
+#ifdef Q_OS_LINUX
+#include
+#endif
+
class T_CORE_EXPORT TSharedMemory
#ifdef Q_OS_WIN
@@ -27,6 +31,17 @@ class T_CORE_EXPORT TSharedMemory
bool unlock();
private:
+ struct header_t {
+#ifdef Q_OS_LINUX
+ pthread_rwlock_t rwlock;
+#endif
+ uint lockcounter {0};
+ };
+
+ bool initRwlock(header_t *header) const;
+ void releaseRwlock(header_t *header) const;
+
+
#ifndef Q_OS_WIN
QString _name;
size_t _size {0};
diff --git a/src/tsharedmemory_linux.cpp b/src/tsharedmemory_linux.cpp
new file mode 100644
index 000000000..43f8d2229
--- /dev/null
+++ b/src/tsharedmemory_linux.cpp
@@ -0,0 +1,98 @@
+/* Copyright (c) 2022, AOYAMA Kazuharu
+ * All rights reserved.
+ *
+ * This software may be used and distributed according to the terms of
+ * the New BSD License, which is incorporated herein by reference.
+ */
+
+#include "tsharedmemory.h"
+#include
+#include
+#include
+
+
+bool TSharedMemory::initRwlock(header_t *header) const
+{
+ int res = -1;
+ pthread_rwlockattr_t attr;
+
+ if (header) {
+ res = pthread_rwlockattr_init(&attr);
+ Q_ASSERT(!res);
+ res = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED); // Linux only
+ Q_ASSERT(!res);
+ if ((res = pthread_rwlock_init(&header->rwlock, &attr)) < 0) {
+ tSystemError("pthread_rwlock_init failed: {}", (const char*)strerror(errno));
+ }
+ }
+ return !res;
+}
+
+
+void TSharedMemory::releaseRwlock(header_t *header) const
+{
+ if (header) {
+ pthread_rwlock_destroy(&header->rwlock);
+ }
+}
+
+
+bool TSharedMemory::lockForRead()
+{
+ struct timespec timeout;
+ header_t *header = (header_t *)_ptr;
+
+ while (pthread_rwlock_tryrdlock(&header->rwlock) == EBUSY) {
+ uint cnt = header->lockcounter;
+ timespec_get(&timeout, TIME_UTC);
+ timeout.tv_sec += 1; // 1sec
+
+ int res = pthread_rwlock_timedrdlock(&header->rwlock, &timeout);
+ if (!res) {
+ // success
+ break;
+ } else {
+ if (res == ETIMEDOUT && header->lockcounter == cnt) {
+ // resets rwlock object
+ initRwlock(header);
+ }
+ }
+ }
+ header->lockcounter++;
+ return true;
+}
+
+
+bool TSharedMemory::lockForWrite()
+{
+ struct timespec timeout;
+ header_t *header = (header_t *)_ptr;
+
+ while (pthread_rwlock_trywrlock(&header->rwlock) == EBUSY) {
+ uint cnt = header->lockcounter;
+ timespec_get(&timeout, TIME_UTC);
+ timeout.tv_sec += 1; // 1sec
+
+ int res = pthread_rwlock_timedwrlock(&header->rwlock, &timeout);
+ if (!res) {
+ // success
+ break;
+ } else {
+ if (res == ETIMEDOUT && header->lockcounter == cnt) {
+ // resets rwlock object
+ releaseRwlock(header);
+ initRwlock(header);
+ }
+ }
+ }
+ header->lockcounter++;
+ return true;
+}
+
+
+bool TSharedMemory::unlock()
+{
+ header_t *header = (header_t *)_ptr;
+ pthread_rwlock_unlock(&header->rwlock);
+ return true;
+}
diff --git a/src/tsharedmemory_macx.cpp b/src/tsharedmemory_macx.cpp
new file mode 100644
index 000000000..1564b8b12
--- /dev/null
+++ b/src/tsharedmemory_macx.cpp
@@ -0,0 +1,120 @@
+/* Copyright (c) 2025, AOYAMA Kazuharu
+ * All rights reserved.
+ *
+ * This software may be used and distributed according to the terms of
+ * the New BSD License, which is incorporated herein by reference.
+ */
+
+#include "tsharedmemory.h"
+#include
+#include
+#include // O_CREAT, O_EXCL
+#include
+#include
+#include
+
+
+static inline QByteArray semaphoreName(const QString &name)
+{
+ return (name.startsWith("/") ? "" : "/") + name.toLatin1() + "_global_lock";
+}
+
+
+bool TSharedMemory::initRwlock(header_t *) const
+{
+ sem_t *sem = sem_open(semaphoreName(_name).data(), O_CREAT | O_EXCL, 0644, 1);
+ if (sem == SEM_FAILED) {
+ if (errno == EEXIST) {
+ tSystemError("sem_open semaphore already exists: {}", semaphoreName(_name));
+ return true;
+ } else {
+ tSystemError("sem_open (init) failed: {}", (const char*)strerror(errno));
+ return false;
+ }
+ }
+
+ tSystemDebug("Semaphore initialized");
+ sem_close(sem);
+ return true;
+}
+
+
+void TSharedMemory::releaseRwlock(header_t *) const
+{
+ sem_unlink(semaphoreName(_name).data());
+}
+
+
+bool TSharedMemory::lockForRead()
+{
+ // Use semaphore as a lock mechanism between processes.
+ // PTHREAD_PROCESS_SHARED attribute is not supported on macos.
+
+ sem_t *sem = SEM_FAILED;
+ header_t *header = (header_t *)_ptr;
+ uint cnt = header->lockcounter;
+
+ auto sem_timedwait = [&](int msecs) {
+ sem = sem_open(semaphoreName(_name).data(), 0);
+ if (sem == SEM_FAILED) {
+ tSystemError("sem_open (lock) failed: {}", (const char*)strerror(errno));
+ return -1;
+ }
+
+ auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(msecs);
+ while (std::chrono::steady_clock::now() < deadline) {
+ if (sem_trywait(sem) < 0) {
+ if (errno == EAGAIN) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ continue;
+ } else {
+ return -1; // error
+ }
+ } else {
+ header->lockcounter++;
+ return 0; // lock success
+ }
+ }
+ tSystemError("sem_wait (lock) timed out: {}", semaphoreName(_name));
+ return 1; // timeout
+ };
+
+ int res;
+ while ((res = sem_timedwait(1000)) == 1) {
+ if (header->lockcounter == cnt) { // timeout and same counter
+ releaseRwlock(header);
+ initRwlock(header);
+ }
+ }
+
+ if (sem != SEM_FAILED) {
+ sem_close(sem);
+ }
+ return !res;
+}
+
+
+bool TSharedMemory::lockForWrite()
+{
+ // Same as lockForRead()
+ return lockForRead();
+}
+
+
+bool TSharedMemory::unlock()
+{
+ sem_t *sem = sem_open(semaphoreName(_name).data(), 0);
+ if (sem == SEM_FAILED) {
+ tSystemError("sem_open (unlock) failed: {}", (const char*)strerror(errno));
+ return false;
+ }
+
+ if (sem_post(sem) < 0) {
+ tSystemError("sem_post failed: {}", (const char*)strerror(errno));
+ sem_close(sem);
+ return false;
+ }
+
+ sem_close(sem);
+ return true;
+}
diff --git a/src/tsharedmemory_qt.cpp b/src/tsharedmemory_qt.cpp
index 81f21bbaa..ca990f55c 100644
--- a/src/tsharedmemory_qt.cpp
+++ b/src/tsharedmemory_qt.cpp
@@ -103,3 +103,13 @@ bool TSharedMemory::unlock()
{
return QSharedMemory::unlock();
}
+
+
+bool TSharedMemory::initRwlock(header_t *) const
+{
+ return true;
+}
+
+
+void TSharedMemory::releaseRwlock(header_t *) const
+{}
diff --git a/src/tsharedmemory_unix.cpp b/src/tsharedmemory_unix.cpp
index 55c0a4a54..4d43e92be 100644
--- a/src/tsharedmemory_unix.cpp
+++ b/src/tsharedmemory_unix.cpp
@@ -12,27 +12,8 @@
#include
#include
#include
-#include
#include
-struct header_t {
- pthread_rwlock_t rwlock;
- uint lockcounter {0};
-};
-
-
-static void rwlock_init(pthread_rwlock_t *rwlock)
-{
- pthread_rwlockattr_t attr;
-
- int res = pthread_rwlockattr_init(&attr);
- Q_ASSERT(!res);
- res = pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
- Q_ASSERT(!res);
- res = pthread_rwlock_init(rwlock, &attr);
- Q_ASSERT(!res);
-}
-
TSharedMemory::TSharedMemory(const QString &name) :
_name(name)
@@ -52,17 +33,12 @@ TSharedMemory::~TSharedMemory()
bool TSharedMemory::create(size_t size)
{
- static const header_t INIT_HEADER = []() {
- static header_t header;
- rwlock_init(&header.rwlock);
- return header;
- }();
-
if (_ptr || size == 0 || _name.isEmpty()) {
return false;
}
struct stat st;
+ header_t *header = nullptr;
// Creates shared memory
_fd = shm_open(qUtf8Printable(_name), O_CREAT | O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR);
@@ -90,7 +66,9 @@ bool TSharedMemory::create(size_t size)
goto error;
}
- std::memcpy(_ptr, &INIT_HEADER, sizeof(INIT_HEADER));
+ header = new (_ptr) header_t{};
+ initRwlock(header);
+
_size = size;
tSystemDebug("SharedMemory created. name:{} size:{}", qUtf8Printable(_name), (qulonglong)_size);
return true;
@@ -111,6 +89,7 @@ bool TSharedMemory::create(size_t size)
void TSharedMemory::unlink()
{
+ releaseRwlock((header_t *)_ptr);
shm_unlink(qUtf8Printable(_name));
tSystemDebug("SharedMemory unlinked. name:{}", qUtf8Printable(_name));
}
@@ -176,6 +155,7 @@ bool TSharedMemory::detach()
_ptr = nullptr;
_size = 0;
+ tSystemDebug("SharedMemory detached. name:{}", qUtf8Printable(_name));
return true;
}
@@ -202,73 +182,3 @@ size_t TSharedMemory::size() const
{
return _size;
}
-
-
-bool TSharedMemory::lockForRead()
-{
-#ifdef Q_OS_LINUX
- struct timespec timeout;
- header_t *header = (header_t *)_ptr;
-
- while (pthread_rwlock_tryrdlock(&header->rwlock) == EBUSY) {
- uint cnt = header->lockcounter;
- timespec_get(&timeout, TIME_UTC);
- timeout.tv_sec += 1; // 1sec
-
- int res = pthread_rwlock_timedrdlock(&header->rwlock, &timeout);
- if (!res) {
- // success
- break;
- } else {
- if (res == ETIMEDOUT && header->lockcounter == cnt) {
- // resets rwlock object
- rwlock_init(&header->rwlock);
- }
- }
- }
- header->lockcounter++;
- return true;
-#else
- header_t *header = (header_t *)_ptr;
- return pthread_rwlock_rdlock(&header->rwlock) == 0;
-#endif
-}
-
-
-bool TSharedMemory::lockForWrite()
-{
-#ifdef Q_OS_LINUX
- struct timespec timeout;
- header_t *header = (header_t *)_ptr;
-
- while (pthread_rwlock_trywrlock(&header->rwlock) == EBUSY) {
- uint cnt = header->lockcounter;
- timespec_get(&timeout, TIME_UTC);
- timeout.tv_sec += 1; // 1sec
-
- int res = pthread_rwlock_timedwrlock(&header->rwlock, &timeout);
- if (!res) {
- // success
- break;
- } else {
- if (res == ETIMEDOUT && header->lockcounter == cnt) {
- // resets rwlock object
- rwlock_init(&header->rwlock);
- }
- }
- }
- header->lockcounter++;
- return true;
-#else
- header_t *header = (header_t *)_ptr;
- return pthread_rwlock_wrlock(&header->rwlock) == 0;
-#endif
-}
-
-
-bool TSharedMemory::unlock()
-{
- header_t *header = (header_t *)_ptr;
- pthread_rwlock_unlock(&header->rwlock);
- return true;
-}
diff --git a/src/tsharedmemorykvs.cpp b/src/tsharedmemorykvs.cpp
index f7106e637..c9d22249b 100644
--- a/src/tsharedmemorykvs.cpp
+++ b/src/tsharedmemorykvs.cpp
@@ -83,16 +83,13 @@ TSharedMemoryKvs::~TSharedMemoryKvs()
*/
bool TSharedMemoryKvs::initialize(const QString &name, const QString &options)
{
- hash_header_t hashheader;
-
TSharedMemoryKvsDriver::initialize(name, options);
TSharedMemoryKvsDriver driver;
- driver.open(name, QString(), QString(), QString(), 0, options);
- hash_header_t *header = (hash_header_t *)driver.origin();
- void *ptr = driver.malloc(sizeof(hashheader));
- Q_ASSERT(ptr == header);
- std::memcpy(header, &hashheader, sizeof(hashheader));
+ driver.open(name, QString(), QString(), QString(), 0, options);
+ void *ptr = driver.malloc(sizeof(hash_header_t));
+ hash_header_t *header = new (ptr) hash_header_t{}; // Initialize with the default constructor
+ Q_ASSERT(header == (hash_header_t *)driver.origin());
ptr = driver.calloc(header->tableSize, sizeof(uintptr_t));
header->setHashg(ptr);
Q_ASSERT(ptr);
diff --git a/src/ttextview.h b/src/ttextview.h
index 57b163b9a..e188f35f8 100644
--- a/src/ttextview.h
+++ b/src/ttextview.h
@@ -33,4 +33,3 @@ inline QString TTextView::toString()
{
return viewText;
}
-
diff --git a/src/tviewhelper.cpp b/src/tviewhelper.cpp
index 647311f2f..50136fb04 100644
--- a/src/tviewhelper.cpp
+++ b/src/tviewhelper.cpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
/*!
@@ -539,9 +540,7 @@ QString TViewHelper::styleSheetTag(const QString &src, const THtmlAttribute &att
QString TViewHelper::styleSheetTag(const QString &src, bool withTimestamp, const THtmlAttribute &attributes) const
{
THtmlAttribute attr = attributes;
- if (!attr.contains("type")) {
- attr.prepend("type", "text/css");
- }
+
if (!attr.contains("rel")) {
attr.prepend("rel", "stylesheet");
}
@@ -549,6 +548,46 @@ QString TViewHelper::styleSheetTag(const QString &src, bool withTimestamp, const
return selfClosingTag("link", attr);
}
+
+static QJsonObject readManifest(const QString &path)
+{
+ QJsonObject json;
+ QFile manifest(path);
+
+ if (manifest.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QJsonParseError error;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(manifest.readAll(), &error);
+ manifest.close();
+
+ if (error.error == QJsonParseError::NoError) {
+ json = jsonDoc.object();
+ } else {
+ tSystemWarn("Manifest parse error [{}]", qUtf8Printable(path));
+ }
+ } else {
+ tSystemWarn("Manifest file not found [{}]", qUtf8Printable(path));
+ }
+ return json;
+}
+
+
+QString TViewHelper::viteStyleSheetTag(const QString &src, const THtmlAttribute &attributes) const
+{
+ constexpr auto MANIFEST_PATH = ".vite/manifest.json";
+ static QJsonObject manifestJson;
+ QString tag;
+
+ if (manifestJson.isEmpty()) {
+ manifestJson = readManifest(Tf::app()->publicPath() + MANIFEST_PATH);
+ }
+
+ auto array = manifestJson.value(src).toObject().value("css").toArray();
+ for (auto item : array) {
+ tag += styleSheetTag("/" + item.toString(), false, attributes);
+ }
+ return tag;
+}
+
/*!
Creates a \
Hello <% QString s(\"%>\"); %>Hello \");\n QString s(\"%>\");\n responsebody += QStringLiteral(\"Hello <%== vvv %>Hello \");\n responsebody += QVariant(vvv).toString();\n responsebody += QStringLiteral(\"Hello <%= vvv %> \nHello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"\\nHello \");\n echo(vvv);\n responsebody += QStringLiteral(\"Hello <%= vvv %> \nHello \");\n eh(vvv);\n responsebody += QStringLiteral(\"\\nHello <%= vvv %>縲\nHello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += tr(\"縲\\nHello \");\n eh(vvv);\n responsebody += tr(\"縲\\nHello <%= vvv; -%> \nHello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\"Hello <% int i; -%> \r\n Hello \");\n int i;\n responsebody += QStringLiteral(\"Hello <% int i; %> \r\nHello ... \t\r\n\tHello ...\\nHello <%= vvv; +%> \nHello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"\\nHello \");\n eh(vvv);\n responsebody += QStringLiteral(\"\\nHello <%= vvv; +%>Hello \");\n responsebody += THttpUtility::htmlEscape(vvv);\n responsebody += QStringLiteral(\"Hello \");\n eh(vvv);\n responsebody += QStringLiteral(\"Hello <% int i; +%> \r\n Hello \");\n int i;\n responsebody += QStringLiteral(\"\\n<%# comment. %|% 33 %>\");\n /* comment. */\n responsebody += QStringLiteral(\"<%= number %|% 33 %>\");\n { QString ___s = QVariant(number).toString(); responsebody += (___s.isEmpty()) ? THttpUtility::htmlEscape(33) : THttpUtility::htmlEscape(___s); }\n responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(number)); if (___s.isEmpty()) { eh(33); } else { eh(number); }}\n responsebody += QStringLiteral(\"<%== number %|% 33 %>\");\n { QString ___s = QVariant(number).toString(); responsebody += (___s.isEmpty()) ? QVariant(33).toString() : ___s; }\n responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(number)); if (___s.isEmpty()) { echo(33); } else { echo(number); }}\n responsebody += QStringLiteral(\"<%=$number %|% 33 %>\");\n tehex2(number, (33));\n responsebody += QStringLiteral(\"<%==$number %|% 33 -%>\t\n\");\n techoex2(number, (33));\n responsebody += QStringLiteral(\"<%== \" %|%\" %|% \"%|%\" -%> \t \n\");\n { QString ___s = QVariant(\" %|%\").toString(); responsebody += (___s.isEmpty()) ? QVariant(\"%|%\").toString() : ___s; }\n responsebody += QStringLiteral(\"\");\n { QString ___s(fromValue(\" %|%\")); if (___s.isEmpty()) { echo(\"%|%\"); } else { echo(\" %|%\"); }}\n responsebody += QStringLiteral(\"