Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

qcomlibrary.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 ** $Id: qcomlibrary.cpp,v 1.2 2003/07/10 02:40:11 llornkcor Exp $
00003 **
00004 ** Implementation of QComLibrary class
00005 **
00006 ** Copyright (C) 2001-2002 Trolltech AS.  All rights reserved.
00007 **
00008 ** This file is part of the tools module of the Qt GUI Toolkit.
00009 **
00010 ** This file may be distributed under the terms of the Q Public License
00011 ** as defined by Trolltech AS of Norway and appearing in the file
00012 ** LICENSE.QPL included in the packaging of this file.
00013 **
00014 ** This file may be distributed and/or modified under the terms of the
00015 ** GNU General Public License version 2 as published by the Free Software
00016 ** Foundation and appearing in the file LICENSE.GPL included in the
00017 ** packaging of this file.
00018 **
00019 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
00020 ** licenses may use this file in accordance with the Qt Commercial License
00021 ** Agreement provided with the Software.
00022 **
00023 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00024 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00025 **
00026 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
00027 **   information about Qt Commercial License Agreements.
00028 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
00029 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00030 **
00031 ** Contact info@trolltech.com if any conditions of this licensing are
00032 ** not clear to you.
00033 **
00034 **********************************************************************/
00035 
00036 #include "qcomlibrary_p.h"
00037 
00038 #ifndef QT_NO_COMPONENT
00039 #include <qapplication.h>
00040 #include <qsettings.h>
00041 #include <qfileinfo.h>
00042 #include <qdatetime.h>
00043 #include <qcleanuphandler.h>
00044 #include <errno.h>
00045 
00046 #ifdef QT_THREAD_SUPPORT
00047 #  include "qmutexpool_p.h"
00048 #endif // QT_THREAD_SUPPORT
00049 
00050 #ifndef QT_DEBUG_COMPONENT
00051 # if defined(QT_DEBUG)
00052 #  define QT_DEBUG_COMPONENT 1
00053 # endif
00054 #endif
00055 
00056 
00057 QComLibrary::QComLibrary( const QString &filename )
00058     : QLibrary( filename ), entry( 0 ), libiface( 0 ), qt_version( 0 )
00059 {
00060 }
00061 
00062 QComLibrary::~QComLibrary()
00063 {
00064     if ( autoUnload() )
00065         unload();
00066     if ( libiface )
00067         libiface->release();
00068     if ( entry )
00069         entry->release();
00070 }
00071 
00072 bool QComLibrary::unload()
00073 {
00074     if ( libiface ) {
00075         libiface->cleanup();
00076         if ( !libiface->canUnload() )
00077             return FALSE;
00078         libiface->release();
00079         libiface = 0;
00080     }
00081     int refs = entry ? entry->release() : 0;
00082     if ( refs )
00083         return FALSE;
00084 
00085     entry = 0;
00086 
00087     return QLibrary::unload();
00088 }
00089 
00090 static bool qt_verify( const QString& library, uint version, uint flags,
00091                     const QCString &key, bool warn )
00092 {
00093     uint our_flags = 1;
00094 #if defined(QT_THREAD_SUPPORT)
00095     our_flags |= 2;
00096 #endif
00097 
00098     if ( (flags & 1) == 0 ) {
00099         if ( warn )
00100             qWarning( "Conflict in %s:\n"
00101                       "  Plugin cannot be queried successfully!",
00102                       (const char*) QFile::encodeName(library) );
00103     } else if ( ( version > QT_VERSION ) ||
00104                 ( ( QT_VERSION & 0xff0000 ) > ( version & 0xff0000 ) ) ) {
00105         if ( warn )
00106             qWarning( "Conflict in %s:\n"
00107                       "  Plugin uses incompatible Qt library (%d.%d.%d)!",
00108                       (const char*) QFile::encodeName(library),
00109                       (version&0xff0000) >> 16, (version&0xff00) >> 8, version&0xff );
00110     } else if ( (flags & 2) != (our_flags & 2) ) {
00111         if ( warn )
00112             qWarning( "Conflict in %s:\n"
00113                       "  Plugin uses %s Qt library!",
00114                       (const char*) QFile::encodeName(library),
00115                       (flags & 2) ? "multi threaded" : "single threaded" );
00116     } else if ( key != QT_BUILD_KEY ) {
00117         if ( warn )
00118             qWarning( "Conflict in %s:\n"
00119                       "  Plugin uses incompatible Qt library!\n"
00120                       "  expected build key \"%s\", got \"%s\".",
00121                       (const char*) QFile::encodeName(library),
00122                       QT_BUILD_KEY,
00123                       key.isEmpty() ? "<null>" : (const char *) key );
00124     } else {
00125         return TRUE;
00126     }
00127     return FALSE;
00128 }
00129 
00130 struct qt_token_info
00131 {
00132     qt_token_info( const char *f, const ulong fc )
00133         : fields( f ), field_count( fc ), results( fc ), lengths( fc )
00134     {
00135         results.fill( 0 );
00136         lengths.fill( 0 );
00137     }
00138 
00139     const char *fields;
00140     const ulong field_count;
00141 
00142     QMemArray<const char *> results;
00143     QMemArray<ulong> lengths;
00144 };
00145 
00146 /*
00147   return values:
00148        1 parse ok
00149        0 eos
00150       -1 parse error
00151 */
00152 static int qt_tokenize( const char *s, ulong s_len, ulong *advance,
00153                         const qt_token_info &token_info )
00154 {
00155     ulong pos = 0, field = 0, fieldlen = 0;
00156     char current;
00157     int ret = -1;
00158     *advance = 0;
00159     for (;;) {
00160         current = s[ pos ];
00161 
00162         // next char
00163         ++pos;
00164         ++fieldlen;
00165         ++*advance;
00166 
00167         if ( ! current || pos == s_len + 1 ) {
00168             // save result
00169             token_info.results[ (int)field ] = s;
00170             token_info.lengths[ (int)field ] = fieldlen - 1;
00171 
00172             // end of string
00173             ret = 0;
00174             break;
00175         }
00176 
00177         if ( current == token_info.fields[ field ] ) {
00178             // save result
00179             token_info.results[ (int)field ] = s;
00180             token_info.lengths[ (int)field ] = fieldlen - 1;
00181 
00182             // end of field
00183             fieldlen = 0;
00184             ++field;
00185             if ( field == token_info.field_count - 1 ) {
00186                 // parse ok
00187                 ret = 1;
00188             }
00189             if ( field == token_info.field_count ) {
00190                 // done parsing
00191                 break;
00192             }
00193 
00194             // reset string and its length
00195             s = s + pos;
00196             s_len -= pos;
00197             pos = 0;
00198         }
00199     }
00200 
00201     return ret;
00202 }
00203 
00204 /*
00205   returns TRUE if the string s was correctly parsed, FALSE otherwise.
00206 */
00207 static bool qt_parse_pattern( const char *s, uint *version, uint *flags,
00208                                    QCString *key )
00209 {
00210     bool ret = TRUE;
00211 
00212     qt_token_info pinfo("=\n", 2);
00213     int parse;
00214     ulong at = 0, advance, parselen = qstrlen( s );
00215     do {
00216         parse = qt_tokenize( s + at, parselen, &advance, pinfo );
00217         if ( parse == -1 ) {
00218             ret = FALSE;
00219             break;
00220         }
00221 
00222         at += advance;
00223         parselen -= advance;
00224 
00225         if ( qstrncmp( "version", pinfo.results[ 0 ], pinfo.lengths[ 0 ] ) == 0 ) {
00226             // parse version string
00227             qt_token_info pinfo2("..-", 3);
00228             if ( qt_tokenize( pinfo.results[ 1 ], pinfo.lengths[ 1 ],
00229                               &advance, pinfo2 ) != -1 ) {
00230                 QCString m( pinfo2.results[ 0 ], pinfo2.lengths[ 0 ] + 1 );
00231                 QCString n( pinfo2.results[ 1 ], pinfo2.lengths[ 1 ] + 1 );
00232                 QCString p( pinfo2.results[ 2 ], pinfo2.lengths[ 2 ] + 1 );
00233                 *version  = (m.toUInt() << 16) | (n.toUInt() << 8) | p.toUInt();
00234             } else {
00235                 ret = FALSE;
00236                 break;
00237             }
00238         } else if ( qstrncmp( "flags", pinfo.results[ 0 ], pinfo.lengths[ 0 ] ) == 0 ) {
00239             // parse flags string
00240             char ch;
00241             *flags = 0;
00242             ulong p = 0, c = 0, bit = 0;
00243             while ( p < pinfo.lengths[ 1 ] ) {
00244                 ch = pinfo.results[ 1 ][ p ];
00245                 bit = pinfo.lengths[ 1 ] - p - 1;
00246                 c = 1 << bit;
00247                 if ( ch == '1' ) {
00248                     *flags |= c;
00249                 } else if ( ch != '0' ) {
00250                     ret = FALSE;
00251                     break;
00252                 }
00253                 ++p;
00254             }
00255         } else if ( qstrncmp( "buildkey", pinfo.results[ 0 ],
00256                               pinfo.lengths[ 0 ] ) == 0 ){
00257             // save buildkey
00258             *key = QCString( pinfo.results[ 1 ], pinfo.lengths[ 1 ] + 1 );
00259         }
00260     } while ( parse == 1 && parselen > 0 );
00261 
00262     return ret;
00263 }
00264 
00265 #if defined(Q_OS_UNIX)
00266 
00267 #if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX)
00268 #  define USE_MMAP
00269 #  include <sys/types.h>
00270 #  include <sys/mman.h>
00271 #endif // Q_OS_FREEBSD || Q_OS_LINUX
00272 
00273 static long qt_find_pattern( const char *s, ulong s_len,
00274                              const char *pattern, ulong p_len )
00275 {
00276     /*
00277       this uses the same algorithm as QString::findRev...
00278 
00279       we search from the end of the file because on the supported
00280       systems, the read-only data/text segments are placed at the end
00281       of the file.  HOWEVER, when building with debugging enabled, all
00282       the debug symbols are placed AFTER the data/text segments.
00283 
00284       what does this mean?  when building in release mode, the search
00285       is fast because the data we are looking for is at the end of the
00286       file... when building in debug mode, the search is slower
00287       because we have to skip over all the debugging symbols first
00288     */
00289 
00290     if ( ! s || ! pattern || p_len > s_len ) return -1;
00291     ulong i, hs = 0, hp = 0, delta = s_len - p_len;
00292 
00293     for (i = 0; i < p_len; ++i ) {
00294         hs += s[delta + i];
00295         hp += pattern[i];
00296     }
00297     i = delta;
00298     for (;;) {
00299         if ( hs == hp && qstrncmp( s + i, pattern, p_len ) == 0 )
00300             return i;
00301         if ( i == 0 )
00302             break;
00303         --i;
00304         hs -= s[i + p_len];
00305         hs += s[i];
00306     }
00307 
00308     return -1;
00309 }
00310 
00311 /*
00312   This opens the specified library, mmaps it into memory, and searches
00313   for the QT_UCM_VERIFICATION_DATA.  The advantage of this approach is that
00314   we can get the verification data without have to actually load the library.
00315   This lets us detect mismatches more safely.
00316 
00317   Returns FALSE if version/flags/key information is not present, or if the
00318                 information could not be read.
00319   Returns  TRUE if version/flags/key information is present and succesfully read.
00320 */
00321 static bool qt_unix_query( const QString &library, uint *version, uint *flags,
00322                            QCString *key )
00323 {
00324     QFile file( library );
00325     if (! file.open( IO_ReadOnly ) ) {
00326         qWarning( "%s: %s", (const char*) QFile::encodeName(library),
00327             strerror( errno ) );
00328         return FALSE;
00329     }
00330 
00331     QByteArray data;
00332     char *filedata = 0;
00333     ulong fdlen = 0;
00334 
00335 #ifdef USE_MMAP
00336     char *mapaddr = 0;
00337     size_t maplen = file.size();
00338     mapaddr = (char *) mmap( mapaddr, maplen, PROT_READ, MAP_PRIVATE, file.handle(), 0 );
00339     if ( mapaddr != MAP_FAILED ) {
00340         // mmap succeeded
00341         filedata = mapaddr;
00342         fdlen = maplen;
00343     } else {
00344         // mmap failed
00345         qWarning( "mmap: %s", strerror( errno ) );
00346 #endif // USE_MMAP
00347         // try reading the data into memory instead
00348         data = file.readAll();
00349         filedata = data.data();
00350         fdlen = data.size();
00351 #ifdef USE_MMAP
00352     }
00353 #endif // USE_MMAP
00354 
00355     // verify that the pattern is present in the plugin
00356     const char *pattern = "pattern=QT_UCM_VERIFICATION_DATA";
00357     const ulong plen = qstrlen( pattern );
00358     long pos = qt_find_pattern( filedata, fdlen, pattern, plen );
00359 
00360     bool ret = FALSE;
00361     if ( pos >= 0 ) {
00362         ret = qt_parse_pattern( filedata + pos, version, flags, key );
00363     }
00364 
00365 #ifdef USE_MMAP
00366     if ( mapaddr != MAP_FAILED && munmap(mapaddr, maplen) != 0 ) {
00367         qWarning( "munmap: %s", strerror( errno ) );
00368     }
00369 #endif // USE_MMAP
00370 
00371     file.close();
00372     return ret;
00373 }
00374 
00375 #endif // Q_OS_UNIX
00376 
00377 
00378 static QSettings *cache = 0;
00379 static QSingleCleanupHandler<QSettings> cleanup_cache;
00380 
00381 void QComLibrary::createInstanceInternal()
00382 {
00383     if ( library().isEmpty() )
00384         return;
00385 
00386     QFileInfo fileinfo( library() );
00387     QString lastModified  = fileinfo.lastModified().toString();
00388     QString regkey = QString("/Qt Plugins %1.%2/%3")
00389                      .arg( ( QT_VERSION & 0xff0000 ) >> 16 )
00390                      .arg( ( QT_VERSION & 0xff00 ) >> 8 )
00391                      .arg( library() );
00392     QStringList reg;
00393     uint flags = 0;
00394     QCString key;
00395     bool query_done = FALSE;
00396     bool warn_mismatch = TRUE;
00397 
00398 #ifdef QT_THREAD_SUPPORT
00399     QMutexLocker locker( qt_global_mutexpool ?
00400                          qt_global_mutexpool->get( &cache ) : 0 );
00401 #endif // QT_THREAD_SUPPORT
00402 
00403     if ( ! cache ) {
00404         cache = new QSettings;
00405         cache->insertSearchPath( QSettings::Windows, "/Trolltech" );
00406         cleanup_cache.set( &cache );
00407     }
00408 
00409     reg = cache->readListEntry( regkey );
00410     if ( reg.count() == 4 ) {
00411         // check timestamp
00412         if ( lastModified == reg[3] ) {
00413             qt_version = reg[0].toUInt(0, 16);
00414             flags = reg[1].toUInt(0, 16);
00415             key = reg[2].latin1();
00416 
00417             query_done = TRUE;
00418             warn_mismatch = FALSE;
00419         }
00420     }
00421 
00422 #if defined(Q_OS_UNIX)
00423     if ( ! query_done ) {
00424         // get the query information directly from the plugin without loading
00425         if ( qt_unix_query( library(), &qt_version, &flags, &key ) ) {
00426             // info read succesfully from library
00427             query_done = TRUE;
00428         }
00429     }
00430 #else // !Q_OS_UNIX
00431     if ( ! query_done ) {
00432         // get the query information by loading the plugin
00433         if ( !isLoaded() ) {
00434             Q_ASSERT( entry == 0 );
00435             if ( !load() )
00436                 return;
00437         }
00438 
00439 #  ifdef Q_CC_BOR
00440         typedef const char * __stdcall (*UCMQueryVerificationDataProc)();
00441 #  else
00442         typedef const char * (*UCMQueryVerificationDataProc)();
00443 #  endif
00444         UCMQueryVerificationDataProc ucmQueryVerificationdataProc;
00445         ucmQueryVerificationdataProc =
00446             (UCMQueryVerificationDataProc) resolve( "qt_ucm_query_verification_data" );
00447 
00448         if ( !ucmQueryVerificationdataProc ||
00449              !qt_parse_pattern( ucmQueryVerificationdataProc(),
00450                                 &qt_version, &flags, &key ) ) {
00451             qt_version = flags = 0;
00452             key = "unknown";
00453         } else {
00454             query_done = TRUE;
00455         }
00456     }
00457 #endif // Q_OS_UNIX
00458 
00459     QStringList queried;
00460     queried << QString::number( qt_version,16 )
00461             << QString::number( flags, 16 )
00462             <<  key
00463             << lastModified;
00464 
00465     if ( queried != reg ) {
00466         cache->writeEntry( regkey, queried );
00467         // delete the cache, which forces the settings to be written
00468         delete cache;
00469         cache = 0;
00470     }
00471 
00472     if ( ! query_done ) {
00473         if ( warn_mismatch ) {
00474             qWarning( "Conflict in %s:\n Plugin cannot be queried successfully!",
00475                       (const char*) QFile::encodeName( library() ) );
00476         }
00477         unload();
00478         return;
00479     }
00480 
00481     if ( ! qt_verify( library(), qt_version, flags, key, warn_mismatch ) ) {
00482         unload();
00483         return;
00484     } else if ( !isLoaded() ) {
00485         Q_ASSERT( entry == 0 );
00486         if ( !load() )
00487             return;
00488     }
00489 
00490 #ifdef Q_CC_BOR
00491     typedef QUnknownInterface* __stdcall (*UCMInstanceProc)();
00492 #else
00493     typedef QUnknownInterface* (*UCMInstanceProc)();
00494 #endif
00495     UCMInstanceProc ucmInstanceProc;
00496     ucmInstanceProc = (UCMInstanceProc) resolve( "ucm_instantiate" );
00497 #if defined(QT_DEBUG_COMPONENT)
00498     if ( !ucmInstanceProc )
00499         qWarning( "%s: Not a UCOM library.", (const char*) QFile::encodeName(library()) );
00500 #endif
00501     entry = ucmInstanceProc ? ucmInstanceProc() : 0;
00502 
00503     if ( entry ) {
00504         if ( entry->queryInterface( IID_QLibrary, (QUnknownInterface**)&libiface ) == QS_OK ) {
00505             if ( libiface && !libiface->init() ) {
00506                 libiface->release();
00507                 libiface = 0;
00508                 unload();
00509                 return;
00510             }
00511         }
00512     } else {
00513 #if defined(QT_DEBUG_COMPONENT)
00514         qWarning( "%s: No exported component provided.", (const char*) QFile::encodeName(library()) );
00515 #endif
00516         unload();
00517     }
00518 }
00519 
00520 QRESULT QComLibrary::queryInterface( const QUuid& request, QUnknownInterface** iface )
00521 {
00522     if ( !entry )
00523         createInstanceInternal();
00524     return entry ? entry->queryInterface( request, iface ) : QE_NOCOMPONENT;
00525 }
00526 
00527 uint QComLibrary::qtVersion()
00528 {
00529     if ( !entry )
00530         createInstanceInternal();
00531     return entry ? qt_version : 0;
00532 }
00533 
00534 
00535 #endif // QT_NO_COMPONENT

Generated on Sat Nov 5 16:18:28 2005 for OPIE by  doxygen 1.4.2