Update SingleApplication and pugixml to latest upstream release

This commit is contained in:
Laurent Trinques
2022-04-27 13:52:41 +02:00
parent 1c99cb5c2d
commit 1e255af3be
10 changed files with 14753 additions and 14647 deletions

View File

@@ -3,6 +3,31 @@ Changelog
If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it. If by accident I have forgotten to credit someone in the CHANGELOG, email me and I will fix it.
__3.3.4__
---------
* Fix compilation under Qt 6.2+ and stricter Qt compile settings. - _Christoph Cullmann_
__3.3.3__
---------
* Support for Qt 6.3+ - Fixed deprecated `QCryptographicHash::addData()` that will only support `QByteArrayView` going further. - _Moody Liu_
__3.3.2__
---------
* Fixed crash caused by sending a `writeAck` on a removed connection. - _Nicolas Werner_
__3.3.1__
---------
* Added support for _AppImage_ dynamic executable paths. - _Michael Klein_
__3.3.0__
---------
* Fixed message fragmentation issue causing crashes and incorrectly / inconsistently received messages. - _Nils Jeisecke_
__3.2.0__ __3.2.0__
--------- ---------

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.7.0) cmake_minimum_required(VERSION 3.12.0)
project(SingleApplication LANGUAGES CXX) project(SingleApplication LANGUAGES CXX)
@@ -38,3 +38,13 @@ endif()
target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS}) target_compile_definitions(${PROJECT_NAME} PUBLIC QAPPLICATION_CLASS=${QAPPLICATION_CLASS})
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(${PROJECT_NAME} PRIVATE
QT_NO_CAST_TO_ASCII
QT_NO_CAST_FROM_ASCII
QT_NO_URL_CAST_FROM_STRING
QT_NO_CAST_FROM_BYTEARRAY
QT_USE_QSTRINGBUILDER
QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
QT_NO_KEYWORDS
QT_NO_FOREACH
)

View File

@@ -35,7 +35,7 @@ To include the library files I would recommend that you add it as a git
submodule to your project. Here is how: submodule to your project. Here is how:
```bash ```bash
git submodule add git@github.com:itay-grudev/SingleApplication.git singleapplication git submodule add https://github.com/itay-grudev/SingleApplication.git singleapplication
``` ```
**Qmake:** **Qmake:**
@@ -182,7 +182,8 @@ bool SingleApplication::sendMessage( QByteArray message, int timeout = 100 )
``` ```
Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout Sends `message` to the Primary Instance. Uses `timeout` as a the maximum timeout
in milliseconds for blocking functions in milliseconds for blocking functions. Returns `true` if the message has been sent
successfully. If the message can't be sent or the function timeouts - returns `false`.
--- ---

View File

@@ -2,7 +2,6 @@
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Allow secondary instances
SingleApplication app( argc, argv ); SingleApplication app( argc, argv );
qWarning() << "Started a new instance"; qWarning() << "Started a new instance";

View File

@@ -248,10 +248,7 @@ bool SingleApplication::sendMessage( const QByteArray &message, int timeout )
if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) ) if( ! d->connectToPrimary( timeout, SingleApplicationPrivate::Reconnect ) )
return false; return false;
d->socket->write( message ); return d->writeConfirmedMessage( timeout, message );
bool dataWritten = d->socket->waitForBytesWritten( timeout );
d->socket->flush();
return dataWritten;
} }
/** /**

View File

@@ -131,21 +131,34 @@ QString SingleApplicationPrivate::getUsername()
void SingleApplicationPrivate::genBlockServerName() void SingleApplicationPrivate::genBlockServerName()
{ {
QCryptographicHash appData( QCryptographicHash::Sha256 ); QCryptographicHash appData( QCryptographicHash::Sha256 );
#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
appData.addData( "SingleApplication", 17 ); appData.addData( "SingleApplication", 17 );
#else
appData.addData( QByteArrayView{"SingleApplication"} );
#endif
appData.addData( SingleApplication::app_t::applicationName().toUtf8() ); appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationName().toUtf8() ); appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() ); appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
if ( ! appDataList.isEmpty() ) if ( ! appDataList.isEmpty() )
appData.addData( appDataList.join( "" ).toUtf8() ); appData.addData( appDataList.join(QString()).toUtf8() );
if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){ if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() ); appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
} }
if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){ if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
#ifdef Q_OS_WIN #if defined(Q_OS_WIN)
appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() ); appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
#elif defined(Q_OS_LINUX)
// If the application is running as an AppImage then the APPIMAGE env var should be used
// instead of applicationPath() as each instance is launched with its own executable path
const QByteArray appImagePath = qgetenv( "APPIMAGE" );
if( appImagePath.isEmpty() ){ // Not running as AppImage: use path to executable file
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
} else { // Running as AppImage: Use absolute path to AppImage file
appData.addData( appImagePath );
};
#else #else
appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() ); appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
#endif #endif
@@ -158,7 +171,7 @@ void SingleApplicationPrivate::genBlockServerName()
// Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
// server naming requirements. // server naming requirements.
blockServerName = appData.result().toBase64().replace("/", "_"); blockServerName = QString::fromUtf8(appData.result().toBase64().replace("/", "_"));
} }
void SingleApplicationPrivate::initializeMemoryBlock() const void SingleApplicationPrivate::initializeMemoryBlock() const
@@ -257,26 +270,52 @@ bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType conne
writeStream << static_cast<quint8>(connectionType); writeStream << static_cast<quint8>(connectionType);
writeStream << instanceNumber; writeStream << instanceNumber;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
quint16 checksum = qChecksum(QByteArray(initMsg, static_cast<quint32>(initMsg.length()))); quint16 checksum = qChecksum(QByteArray(initMsg.constData(), static_cast<quint32>(initMsg.length())));
#else #else
quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length())); quint16 checksum = qChecksum(initMsg.constData(), static_cast<quint32>(initMsg.length()));
#endif #endif
writeStream << checksum; writeStream << checksum;
// The header indicates the message length that follows return writeConfirmedMessage( static_cast<int>(msecs - time.elapsed()), initMsg );
}
void SingleApplicationPrivate::writeAck( QLocalSocket *sock ) {
sock->putChar('\n');
}
bool SingleApplicationPrivate::writeConfirmedMessage (int msecs, const QByteArray &msg)
{
QElapsedTimer time;
time.start();
// Frame 1: The header indicates the message length that follows
QByteArray header; QByteArray header;
QDataStream headerStream(&header, QIODevice::WriteOnly); QDataStream headerStream(&header, QIODevice::WriteOnly);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
headerStream.setVersion(QDataStream::Qt_5_6); headerStream.setVersion(QDataStream::Qt_5_6);
#endif #endif
headerStream << static_cast <quint64>( initMsg.length() ); headerStream << static_cast <quint64>( msg.length() );
socket->write( header ); if( ! writeConfirmedFrame( static_cast<int>(msecs - time.elapsed()), header ))
socket->write( initMsg ); return false;
bool result = socket->waitForBytesWritten( static_cast<int>(msecs - time.elapsed()) );
// Frame 2: The message
return writeConfirmedFrame( static_cast<int>(msecs - time.elapsed()), msg );
}
bool SingleApplicationPrivate::writeConfirmedFrame( int msecs, const QByteArray &msg )
{
socket->write( msg );
socket->flush(); socket->flush();
return result;
bool result = socket->waitForReadyRead( msecs ); // await ack byte
if (result) {
socket->read( 1 );
return true;
}
return false;
} }
quint16 SingleApplicationPrivate::blockChecksum() const quint16 SingleApplicationPrivate::blockChecksum() const
@@ -321,33 +360,36 @@ void SingleApplicationPrivate::slotConnectionEstablished()
QLocalSocket *nextConnSocket = server->nextPendingConnection(); QLocalSocket *nextConnSocket = server->nextPendingConnection();
connectionMap.insert(nextConnSocket, ConnectionInfo()); connectionMap.insert(nextConnSocket, ConnectionInfo());
QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this,
[nextConnSocket, this](){ [nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket]; auto &info = connectionMap[nextConnSocket];
Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId ); this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
} }
); );
QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater); QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
QObject::connect(nextConnSocket, &QLocalSocket::destroyed, QObject::connect(nextConnSocket, &QLocalSocket::destroyed, this,
[nextConnSocket, this](){ [nextConnSocket, this](){
connectionMap.remove(nextConnSocket); connectionMap.remove(nextConnSocket);
} }
); );
QObject::connect(nextConnSocket, &QLocalSocket::readyRead, QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this,
[nextConnSocket, this](){ [nextConnSocket, this](){
auto &info = connectionMap[nextConnSocket]; auto &info = connectionMap[nextConnSocket];
switch(info.stage){ switch(info.stage){
case StageHeader: case StageInitHeader:
readInitMessageHeader(nextConnSocket); readMessageHeader( nextConnSocket, StageInitBody );
break; break;
case StageBody: case StageInitBody:
readInitMessageBody(nextConnSocket); readInitMessageBody(nextConnSocket);
break; break;
case StageConnected: case StageConnectedHeader:
Q_EMIT this->slotDataAvailable( nextConnSocket, info.instanceId ); readMessageHeader( nextConnSocket, StageConnectedBody );
break;
case StageConnectedBody:
this->slotDataAvailable( nextConnSocket, info.instanceId );
break; break;
default: default:
break; break;
@@ -356,7 +398,7 @@ void SingleApplicationPrivate::slotConnectionEstablished()
); );
} }
void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock ) void SingleApplicationPrivate::readMessageHeader( QLocalSocket *sock, SingleApplicationPrivate::ConnectionStage nextStage )
{ {
if (!connectionMap.contains( sock )){ if (!connectionMap.contains( sock )){
return; return;
@@ -376,29 +418,35 @@ void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock )
quint64 msgLen = 0; quint64 msgLen = 0;
headerStream >> msgLen; headerStream >> msgLen;
ConnectionInfo &info = connectionMap[sock]; ConnectionInfo &info = connectionMap[sock];
info.stage = StageBody; info.stage = nextStage;
info.msgLen = msgLen; info.msgLen = msgLen;
if ( sock->bytesAvailable() >= (qint64) msgLen ){ writeAck( sock );
readInitMessageBody( sock ); }
bool SingleApplicationPrivate::isFrameComplete( QLocalSocket *sock )
{
if (!connectionMap.contains( sock )){
return false;
} }
ConnectionInfo &info = connectionMap[sock];
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
return false;
}
return true;
} }
void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock ) void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
{ {
Q_Q(SingleApplication); Q_Q(SingleApplication);
if (!connectionMap.contains( sock )){ if( !isFrameComplete( sock ) )
return; return;
}
ConnectionInfo &info = connectionMap[sock];
if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
return;
}
// Read the message body // Read the message body
QByteArray msgBytes = sock->read(info.msgLen); QByteArray msgBytes = sock->readAll();
QDataStream readStream(msgBytes); QDataStream readStream(msgBytes);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
@@ -424,7 +472,7 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
readStream >> msgChecksum; readStream >> msgChecksum;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
const quint16 actualChecksum = qChecksum(QByteArray(msgBytes, static_cast<quint32>(msgBytes.length() - sizeof(quint16)))); const quint16 actualChecksum = qChecksum(QByteArray(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
#else #else
const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16))); const quint16 actualChecksum = qChecksum(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16)));
#endif #endif
@@ -438,8 +486,9 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
return; return;
} }
ConnectionInfo &info = connectionMap[sock];
info.instanceId = instanceId; info.instanceId = instanceId;
info.stage = StageConnected; info.stage = StageConnectedHeader;
if( connectionType == NewInstance || if( connectionType == NewInstance ||
( connectionType == SecondaryInstance && ( connectionType == SecondaryInstance &&
@@ -448,21 +497,30 @@ void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
Q_EMIT q->instanceStarted(); Q_EMIT q->instanceStarted();
} }
if (sock->bytesAvailable() > 0){ writeAck( sock );
Q_EMIT this->slotDataAvailable( sock, instanceId );
}
} }
void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId ) void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
{ {
Q_Q(SingleApplication); Q_Q(SingleApplication);
Q_EMIT q->receivedMessage( instanceId, dataSocket->readAll() );
if ( !isFrameComplete( dataSocket ) )
return;
const QByteArray message = dataSocket->readAll();
writeAck( dataSocket );
ConnectionInfo &info = connectionMap[dataSocket];
info.stage = StageConnectedHeader;
Q_EMIT q->receivedMessage( instanceId, message);
} }
void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId ) void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
{ {
if( closedSocket->bytesAvailable() > 0 ) if( closedSocket->bytesAvailable() > 0 )
Q_EMIT slotDataAvailable( closedSocket, instanceId ); slotDataAvailable( closedSocket, instanceId );
} }
void SingleApplicationPrivate::randomSleep() void SingleApplicationPrivate::randomSleep()
@@ -471,7 +529,7 @@ void SingleApplicationPrivate::randomSleep()
QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u )); QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
#else #else
qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() ); qsrand( QDateTime::currentMSecsSinceEpoch() % std::numeric_limits<uint>::max() );
QThread::msleep( 8 + static_cast <unsigned long>( static_cast <float>( qrand() ) / RAND_MAX * 10 )); QThread::msleep( qrand() % 11 + 8);
#endif #endif
} }

View File

@@ -61,9 +61,10 @@ public:
Reconnect = 3 Reconnect = 3
}; };
enum ConnectionStage : quint8 { enum ConnectionStage : quint8 {
StageHeader = 0, StageInitHeader = 0,
StageBody = 1, StageInitBody = 1,
StageConnected = 2, StageConnectedHeader = 2,
StageConnectedBody = 3,
}; };
Q_DECLARE_PUBLIC(SingleApplication) Q_DECLARE_PUBLIC(SingleApplication)
@@ -79,8 +80,12 @@ public:
quint16 blockChecksum() const; quint16 blockChecksum() const;
qint64 primaryPid() const; qint64 primaryPid() const;
QString primaryUser() const; QString primaryUser() const;
void readInitMessageHeader(QLocalSocket *socket); bool isFrameComplete(QLocalSocket *sock);
void readMessageHeader(QLocalSocket *socket, ConnectionStage nextStage);
void readInitMessageBody(QLocalSocket *socket); void readInitMessageBody(QLocalSocket *socket);
void writeAck(QLocalSocket *sock);
bool writeConfirmedFrame(int msecs, const QByteArray &msg);
bool writeConfirmedMessage(int msecs, const QByteArray &msg);
static void randomSleep(); static void randomSleep();
void addAppData(const QString &data); void addAppData(const QString &data);
QStringList appData() const; QStringList appData() const;

View File

@@ -1,77 +1,77 @@
/** /**
* pugixml parser - version 1.11 * pugixml parser - version 1.12
* -------------------------------------------------------- * --------------------------------------------------------
* Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Copyright (C) 2006-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at https://pugixml.org/ * Report bugs and download new versions at https://pugixml.org/
* *
* This library is distributed under the MIT License. See notice at the end * This library is distributed under the MIT License. See notice at the end
* of this file. * of this file.
* *
* This work is based on the pugxml parser, which is: * This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/ */
#ifndef HEADER_PUGICONFIG_HPP #ifndef HEADER_PUGICONFIG_HPP
#define HEADER_PUGICONFIG_HPP #define HEADER_PUGICONFIG_HPP
// Uncomment this to enable wchar_t mode // Uncomment this to enable wchar_t mode
// #define PUGIXML_WCHAR_MODE // #define PUGIXML_WCHAR_MODE
// Uncomment this to enable compact mode // Uncomment this to enable compact mode
// #define PUGIXML_COMPACT // #define PUGIXML_COMPACT
// Uncomment this to disable XPath // Uncomment this to disable XPath
// #define PUGIXML_NO_XPATH // #define PUGIXML_NO_XPATH
// Uncomment this to disable STL // Uncomment this to disable STL
// #define PUGIXML_NO_STL // #define PUGIXML_NO_STL
// Uncomment this to disable exceptions // Uncomment this to disable exceptions
// #define PUGIXML_NO_EXCEPTIONS // #define PUGIXML_NO_EXCEPTIONS
// Set this to control attributes for public classes/functions, i.e.: // Set this to control attributes for public classes/functions, i.e.:
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL // #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL // #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
// Tune these constants to adjust memory-related behavior // Tune these constants to adjust memory-related behavior
// #define PUGIXML_MEMORY_PAGE_SIZE 32768 // #define PUGIXML_MEMORY_PAGE_SIZE 32768
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_OUTPUT_STACK 10240
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
// Tune this constant to adjust max nesting for XPath queries // Tune this constant to adjust max nesting for XPath queries
// #define PUGIXML_XPATH_DEPTH_LIMIT 1024 // #define PUGIXML_XPATH_DEPTH_LIMIT 1024
// Uncomment this to switch to header-only version // Uncomment this to switch to header-only version
// #define PUGIXML_HEADER_ONLY // #define PUGIXML_HEADER_ONLY
// Uncomment this to enable long long support // Uncomment this to enable long long support
// #define PUGIXML_HAS_LONG_LONG // #define PUGIXML_HAS_LONG_LONG
#endif #endif
/** /**
* Copyright (c) 2006-2020 Arseny Kapoulkine * Copyright (c) 2006-2022 Arseny Kapoulkine
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation * obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without * files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, * restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell * copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the * copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following * Software is furnished to do so, subject to the following
* conditions: * conditions:
* *
* The above copyright notice and this permission notice shall be * The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software. * included in all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE. * OTHER DEALINGS IN THE SOFTWARE.
*/ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff