summaryrefslogtreecommitdiffstats
path: root/src/tools/qcomlibrary.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/qcomlibrary.cpp')
-rw-r--r--src/tools/qcomlibrary.cpp538
1 files changed, 538 insertions, 0 deletions
diff --git a/src/tools/qcomlibrary.cpp b/src/tools/qcomlibrary.cpp
new file mode 100644
index 0000000..8d13549
--- /dev/null
+++ b/src/tools/qcomlibrary.cpp
@@ -0,0 +1,538 @@
+/****************************************************************************
+**
+** Implementation of QComLibrary class
+**
+** Copyright (C) 2001-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the tools module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at [email protected].
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#include "qcomlibrary_p.h"
+
+#ifndef QT_NO_COMPONENT
+#include <qapplication.h>
+#include <qsettings.h>
+#include <qfileinfo.h>
+#include <qdatetime.h>
+#include <qcleanuphandler.h>
+#ifndef NO_ERRNO_H
+#include <errno.h>
+#endif // NO_ERROR_H
+
+#ifdef QT_THREAD_SUPPORT
+# include "qmutexpool_p.h"
+#endif // QT_THREAD_SUPPORT
+
+#ifndef QT_DEBUG_COMPONENT
+# if defined(QT_DEBUG)
+# define QT_DEBUG_COMPONENT 1
+# endif
+#endif
+
+
+QComLibrary::QComLibrary( const QString &filename )
+ : QLibrary( filename ), entry( 0 ), libiface( 0 ), qt_version( 0 )
+{
+}
+
+QComLibrary::~QComLibrary()
+{
+ if ( autoUnload() )
+ unload();
+ if ( libiface )
+ libiface->release();
+ if ( entry )
+ entry->release();
+}
+
+bool QComLibrary::unload()
+{
+ int refs = entry ? entry->release() : 0;
+ entry = 0;
+ if (refs || !libiface)
+ return FALSE;
+
+ libiface->cleanup();
+ if ( !libiface->canUnload() )
+ return FALSE;
+ libiface->release();
+ libiface = 0;
+
+ return QLibrary::unload();
+}
+
+static bool qt_verify( const QString& library, uint version, uint flags,
+ const QCString &key, bool warn )
+{
+ uint our_flags = 1;
+#if defined(QT_THREAD_SUPPORT)
+ our_flags |= 2;
+#endif
+
+ if ( (flags & 1) == 0 ) {
+ if ( warn )
+ qWarning( "Conflict in %s:\n"
+ " Plugin cannot be queried successfully!",
+ (const char*) QFile::encodeName(library) );
+ } else if ( ( version > QT_VERSION ) ||
+ ( ( QT_VERSION & 0xff0000 ) > ( version & 0xff0000 ) ) ) {
+ if ( warn )
+ qWarning( "Conflict in %s:\n"
+ " Plugin uses incompatible Qt library (%d.%d.%d)!",
+ (const char*) QFile::encodeName(library),
+ (version&0xff0000) >> 16, (version&0xff00) >> 8, version&0xff );
+ } else if ( (flags & 2) != (our_flags & 2) ) {
+ if ( warn )
+ qWarning( "Conflict in %s:\n"
+ " Plugin uses %s Qt library!",
+ (const char*) QFile::encodeName(library),
+ (flags & 2) ? "multi threaded" : "single threaded" );
+ } else if ( key != QT_BUILD_KEY ) {
+ if ( warn )
+ qWarning( "Conflict in %s:\n"
+ " Plugin uses incompatible Qt library!\n"
+ " expected build key \"%s\", got \"%s\".",
+ (const char*) QFile::encodeName(library),
+ QT_BUILD_KEY,
+ key.isEmpty() ? "<null>" : (const char *) key );
+ } else {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+struct qt_token_info
+{
+ qt_token_info( const char *f, const ulong fc )
+ : fields( f ), field_count( fc ), results( fc ), lengths( fc )
+ {
+ results.fill( 0 );
+ lengths.fill( 0 );
+ }
+
+ const char *fields;
+ const ulong field_count;
+
+ QMemArray<const char *> results;
+ QMemArray<ulong> lengths;
+};
+
+/*
+ return values:
+ 1 parse ok
+ 0 eos
+ -1 parse error
+*/
+static int qt_tokenize( const char *s, ulong s_len, ulong *advance,
+ const qt_token_info &token_info )
+{
+ ulong pos = 0, field = 0, fieldlen = 0;
+ char current;
+ int ret = -1;
+ *advance = 0;
+ for (;;) {
+ current = s[ pos ];
+
+ // next char
+ ++pos;
+ ++fieldlen;
+ ++*advance;
+
+ if ( ! current || pos == s_len + 1 ) {
+ // save result
+ token_info.results[ (int)field ] = s;
+ token_info.lengths[ (int)field ] = fieldlen - 1;
+
+ // end of string
+ ret = 0;
+ break;
+ }
+
+ if ( current == token_info.fields[ field ] ) {
+ // save result
+ token_info.results[ (int)field ] = s;
+ token_info.lengths[ (int)field ] = fieldlen - 1;
+
+ // end of field
+ fieldlen = 0;
+ ++field;
+ if ( field == token_info.field_count - 1 ) {
+ // parse ok
+ ret = 1;
+ }
+ if ( field == token_info.field_count ) {
+ // done parsing
+ break;
+ }
+
+ // reset string and its length
+ s = s + pos;
+ s_len -= pos;
+ pos = 0;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ returns TRUE if the string s was correctly parsed, FALSE otherwise.
+*/
+static bool qt_parse_pattern( const char *s, uint *version, uint *flags,
+ QCString *key )
+{
+ bool ret = TRUE;
+
+ qt_token_info pinfo("=\n", 2);
+ int parse;
+ ulong at = 0, advance, parselen = qstrlen( s );
+ do {
+ parse = qt_tokenize( s + at, parselen, &advance, pinfo );
+ if ( parse == -1 ) {
+ ret = FALSE;
+ break;
+ }
+
+ at += advance;
+ parselen -= advance;
+
+ if ( qstrncmp( "version", pinfo.results[ 0 ], pinfo.lengths[ 0 ] ) == 0 ) {
+ // parse version string
+ qt_token_info pinfo2("..-", 3);
+ if ( qt_tokenize( pinfo.results[ 1 ], pinfo.lengths[ 1 ],
+ &advance, pinfo2 ) != -1 ) {
+ QCString m( pinfo2.results[ 0 ], pinfo2.lengths[ 0 ] + 1 );
+ QCString n( pinfo2.results[ 1 ], pinfo2.lengths[ 1 ] + 1 );
+ QCString p( pinfo2.results[ 2 ], pinfo2.lengths[ 2 ] + 1 );
+ *version = (m.toUInt() << 16) | (n.toUInt() << 8) | p.toUInt();
+ } else {
+ ret = FALSE;
+ break;
+ }
+ } else if ( qstrncmp( "flags", pinfo.results[ 0 ], pinfo.lengths[ 0 ] ) == 0 ) {
+ // parse flags string
+ char ch;
+ *flags = 0;
+ ulong p = 0, c = 0, bit = 0;
+ while ( p < pinfo.lengths[ 1 ] ) {
+ ch = pinfo.results[ 1 ][ p ];
+ bit = pinfo.lengths[ 1 ] - p - 1;
+ c = 1 << bit;
+ if ( ch == '1' ) {
+ *flags |= c;
+ } else if ( ch != '0' ) {
+ ret = FALSE;
+ break;
+ }
+ ++p;
+ }
+ } else if ( qstrncmp( "buildkey", pinfo.results[ 0 ],
+ pinfo.lengths[ 0 ] ) == 0 ){
+ // save buildkey
+ *key = QCString( pinfo.results[ 1 ], pinfo.lengths[ 1 ] + 1 );
+ }
+ } while ( parse == 1 && parselen > 0 );
+
+ return ret;
+}
+
+#if defined(Q_OS_UNIX)
+
+#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX)
+# define USE_MMAP
+# include <sys/types.h>
+# include <sys/mman.h>
+#endif // Q_OS_FREEBSD || Q_OS_LINUX
+
+static long qt_find_pattern( const char *s, ulong s_len,
+ const char *pattern, ulong p_len )
+{
+ /*
+ this uses the same algorithm as QString::findRev...
+
+ we search from the end of the file because on the supported
+ systems, the read-only data/text segments are placed at the end
+ of the file. HOWEVER, when building with debugging enabled, all
+ the debug symbols are placed AFTER the data/text segments.
+
+ what does this mean? when building in release mode, the search
+ is fast because the data we are looking for is at the end of the
+ file... when building in debug mode, the search is slower
+ because we have to skip over all the debugging symbols first
+ */
+
+ if ( ! s || ! pattern || p_len > s_len ) return -1;
+ ulong i, hs = 0, hp = 0, delta = s_len - p_len;
+
+ for (i = 0; i < p_len; ++i ) {
+ hs += s[delta + i];
+ hp += pattern[i];
+ }
+ i = delta;
+ for (;;) {
+ if ( hs == hp && qstrncmp( s + i, pattern, p_len ) == 0 )
+ return i;
+ if ( i == 0 )
+ break;
+ --i;
+ hs -= s[i + p_len];
+ hs += s[i];
+ }
+
+ return -1;
+}
+
+/*
+ This opens the specified library, mmaps it into memory, and searches
+ for the QT_UCM_VERIFICATION_DATA. The advantage of this approach is that
+ we can get the verification data without have to actually load the library.
+ This lets us detect mismatches more safely.
+
+ Returns FALSE if version/flags/key information is not present, or if the
+ information could not be read.
+ Returns TRUE if version/flags/key information is present and succesfully read.
+*/
+static bool qt_unix_query( const QString &library, uint *version, uint *flags,
+ QCString *key )
+{
+ QFile file( library );
+ if (! file.open( IO_ReadOnly ) ) {
+ qWarning( "%s: %s", (const char*) QFile::encodeName(library),
+ strerror( errno ) );
+ return FALSE;
+ }
+
+ QByteArray data;
+ char *filedata = 0;
+ ulong fdlen = 0;
+
+#ifdef USE_MMAP
+ char *mapaddr = 0;
+ size_t maplen = file.size();
+ mapaddr = (char *) mmap( mapaddr, maplen, PROT_READ, MAP_PRIVATE, file.handle(), 0 );
+ if ( mapaddr != MAP_FAILED ) {
+ // mmap succeeded
+ filedata = mapaddr;
+ fdlen = maplen;
+ } else {
+ // mmap failed
+ qWarning( "mmap: %s", strerror( errno ) );
+#endif // USE_MMAP
+ // try reading the data into memory instead
+ data = file.readAll();
+ filedata = data.data();
+ fdlen = data.size();
+#ifdef USE_MMAP
+ }
+#endif // USE_MMAP
+
+ // verify that the pattern is present in the plugin
+ const char *pattern = "pattern=QT_UCM_VERIFICATION_DATA";
+ const ulong plen = qstrlen( pattern );
+ long pos = qt_find_pattern( filedata, fdlen, pattern, plen );
+
+ bool ret = FALSE;
+ if ( pos >= 0 ) {
+ ret = qt_parse_pattern( filedata + pos, version, flags, key );
+ }
+
+#ifdef USE_MMAP
+ if ( mapaddr != MAP_FAILED && munmap(mapaddr, maplen) != 0 ) {
+ qWarning( "munmap: %s", strerror( errno ) );
+ }
+#endif // USE_MMAP
+
+ file.close();
+ return ret;
+}
+
+#endif // Q_OS_UNIX
+
+
+static QSettings *cache = 0;
+static QSingleCleanupHandler<QSettings> cleanup_cache;
+
+void QComLibrary::createInstanceInternal()
+{
+ if ( library().isEmpty() )
+ return;
+
+ QFileInfo fileinfo( library() );
+ QString lastModified = fileinfo.lastModified().toString(Qt::ISODate);
+ QString regkey = QString("/Qt Plugins %1.%2/%3")
+ .arg( ( QT_VERSION & 0xff0000 ) >> 16 )
+ .arg( ( QT_VERSION & 0xff00 ) >> 8 )
+ .arg( library() );
+ QStringList reg;
+ uint flags = 0;
+ QCString key;
+ bool query_done = FALSE;
+ bool warn_mismatch = TRUE;
+
+#ifdef QT_THREAD_SUPPORT
+ QMutexLocker locker( qt_global_mutexpool ?
+ qt_global_mutexpool->get( &cache ) : 0 );
+#endif // QT_THREAD_SUPPORT
+
+ if ( ! cache ) {
+ cache = new QSettings;
+ cache->insertSearchPath( QSettings::Windows, "/Trolltech" );
+ cleanup_cache.set( &cache );
+ }
+
+ reg = cache->readListEntry( regkey );
+ if ( reg.count() == 4 ) {
+ // check timestamp
+ if ( lastModified == reg[3] ) {
+ qt_version = reg[0].toUInt(0, 16);
+ flags = reg[1].toUInt(0, 16);
+ key = reg[2].latin1();
+
+ query_done = TRUE;
+ warn_mismatch = FALSE;
+ }
+ }
+
+#if defined(Q_OS_UNIX)
+ if ( ! query_done ) {
+ // get the query information directly from the plugin without loading
+ if ( qt_unix_query( library(), &qt_version, &flags, &key ) ) {
+ // info read succesfully from library
+ query_done = TRUE;
+ }
+ }
+#else // !Q_OS_UNIX
+ if ( ! query_done ) {
+ // get the query information by loading the plugin
+ if ( !isLoaded() ) {
+ Q_ASSERT( entry == 0 );
+ if ( !load() )
+ return;
+ }
+
+# ifdef Q_CC_BOR
+ typedef const char * __stdcall (*UCMQueryVerificationDataProc)();
+# else
+ typedef const char * (*UCMQueryVerificationDataProc)();
+# endif
+ UCMQueryVerificationDataProc ucmQueryVerificationdataProc;
+ ucmQueryVerificationdataProc =
+ (UCMQueryVerificationDataProc) resolve( "qt_ucm_query_verification_data" );
+
+ if ( !ucmQueryVerificationdataProc ||
+ !qt_parse_pattern( ucmQueryVerificationdataProc(),
+ &qt_version, &flags, &key ) ) {
+ qt_version = flags = 0;
+ key = "unknown";
+ } else {
+ query_done = TRUE;
+ }
+ }
+#endif // Q_OS_UNIX
+
+ QStringList queried;
+ queried << QString::number( qt_version,16 )
+ << QString::number( flags, 16 )
+ << key
+ << lastModified;
+
+ if ( queried != reg ) {
+ cache->writeEntry( regkey, queried );
+ // delete the cache, which forces the settings to be written
+ delete cache;
+ cache = 0;
+ }
+
+ if ( ! query_done ) {
+ if ( warn_mismatch ) {
+ qWarning( "Conflict in %s:\n Plugin cannot be queried successfully!",
+ (const char*) QFile::encodeName( library() ) );
+ }
+ unload();
+ return;
+ }
+
+ if ( ! qt_verify( library(), qt_version, flags, key, warn_mismatch ) ) {
+ unload();
+ return;
+ } else if ( !isLoaded() ) {
+ Q_ASSERT( entry == 0 );
+ if ( !load() )
+ return;
+ }
+
+#ifdef Q_CC_BOR
+ typedef QUnknownInterface* __stdcall (*UCMInstanceProc)();
+#else
+ typedef QUnknownInterface* (*UCMInstanceProc)();
+#endif
+ UCMInstanceProc ucmInstanceProc;
+ ucmInstanceProc = (UCMInstanceProc) resolve( "ucm_instantiate" );
+#if defined(QT_DEBUG_COMPONENT)
+ if ( !ucmInstanceProc )
+ qWarning( "%s: Not a UCOM library.", (const char*) QFile::encodeName(library()) );
+#endif
+ entry = ucmInstanceProc ? ucmInstanceProc() : 0;
+
+ if ( entry ) {
+ if ( entry->queryInterface( IID_QLibrary, (QUnknownInterface**)&libiface ) == QS_OK ) {
+ if ( libiface && !libiface->init() ) {
+ libiface->release();
+ libiface = 0;
+ unload();
+ return;
+ }
+ }
+ } else {
+#if defined(QT_DEBUG_COMPONENT)
+ qWarning( "%s: No exported component provided.", (const char*) QFile::encodeName(library()) );
+#endif
+ unload();
+ }
+}
+
+QRESULT QComLibrary::queryInterface( const QUuid& request, QUnknownInterface** iface )
+{
+ if ( !entry )
+ createInstanceInternal();
+ return entry ? entry->queryInterface( request, iface ) : QE_NOCOMPONENT;
+}
+
+uint QComLibrary::qtVersion()
+{
+ if ( !entry )
+ createInstanceInternal();
+ return entry ? qt_version : 0;
+}
+
+
+#endif // QT_NO_COMPONENT