source: vendor/trolltech/current/src/tools/qcomlibrary.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 14.3 KB
Line 
1/****************************************************************************
2** $Id: qcomlibrary.cpp 2 2005-11-16 15:49:26Z dmik $
3**
4** Implementation of QComLibrary class
5**
6** Copyright (C) 2001-2002 Trolltech AS. All rights reserved.
7**
8** This file is part of the tools module of the Qt GUI Toolkit.
9**
10** This file may be distributed under the terms of the Q Public License
11** as defined by Trolltech AS of Norway and appearing in the file
12** LICENSE.QPL included in the packaging of this file.
13**
14** This file may be distributed and/or modified under the terms of the
15** GNU General Public License version 2 as published by the Free Software
16** Foundation and appearing in the file LICENSE.GPL included in the
17** packaging of this file.
18**
19** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
20** licenses may use this file in accordance with the Qt Commercial License
21** Agreement provided with the Software.
22**
23** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
24** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25**
26** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
27** information about Qt Commercial License Agreements.
28** See http://www.trolltech.com/qpl/ for QPL licensing information.
29** See http://www.trolltech.com/gpl/ for GPL licensing information.
30**
31** Contact info@trolltech.com if any conditions of this licensing are
32** not clear to you.
33**
34**********************************************************************/
35
36#include "qcomlibrary_p.h"
37
38#ifndef QT_NO_COMPONENT
39#include <qapplication.h>
40#include <qsettings.h>
41#include <qfileinfo.h>
42#include <qdatetime.h>
43#include <qcleanuphandler.h>
44#ifndef NO_ERRNO_H
45#include <errno.h>
46#endif // NO_ERROR_H
47
48#ifdef QT_THREAD_SUPPORT
49# include "qmutexpool_p.h"
50#endif // QT_THREAD_SUPPORT
51
52#ifndef QT_DEBUG_COMPONENT
53# if defined(QT_DEBUG)
54# define QT_DEBUG_COMPONENT 1
55# endif
56#endif
57
58
59QComLibrary::QComLibrary( const QString &filename )
60 : QLibrary( filename ), entry( 0 ), libiface( 0 ), qt_version( 0 )
61{
62}
63
64QComLibrary::~QComLibrary()
65{
66 if ( autoUnload() )
67 unload();
68 if ( libiface )
69 libiface->release();
70 if ( entry )
71 entry->release();
72}
73
74bool QComLibrary::unload()
75{
76 int refs = entry ? entry->release() : 0;
77 entry = 0;
78 if (refs || !libiface)
79 return FALSE;
80
81 libiface->cleanup();
82 if ( !libiface->canUnload() )
83 return FALSE;
84 libiface->release();
85 libiface = 0;
86
87 return QLibrary::unload();
88}
89
90static bool qt_verify( const QString& library, uint version, uint flags,
91 const QCString &key, bool warn )
92{
93 uint our_flags = 1;
94#if defined(QT_THREAD_SUPPORT)
95 our_flags |= 2;
96#endif
97
98 if ( (flags & 1) == 0 ) {
99 if ( warn )
100 qWarning( "Conflict in %s:\n"
101 " Plugin cannot be queried successfully!",
102 (const char*) QFile::encodeName(library) );
103 } else if ( ( version > QT_VERSION ) ||
104 ( ( QT_VERSION & 0xff0000 ) > ( version & 0xff0000 ) ) ) {
105 if ( warn )
106 qWarning( "Conflict in %s:\n"
107 " Plugin uses incompatible Qt library (%d.%d.%d)!",
108 (const char*) QFile::encodeName(library),
109 (version&0xff0000) >> 16, (version&0xff00) >> 8, version&0xff );
110 } else if ( (flags & 2) != (our_flags & 2) ) {
111 if ( warn )
112 qWarning( "Conflict in %s:\n"
113 " Plugin uses %s Qt library!",
114 (const char*) QFile::encodeName(library),
115 (flags & 2) ? "multi threaded" : "single threaded" );
116 } else if ( key != QT_BUILD_KEY ) {
117 if ( warn )
118 qWarning( "Conflict in %s:\n"
119 " Plugin uses incompatible Qt library!\n"
120 " expected build key \"%s\", got \"%s\".",
121 (const char*) QFile::encodeName(library),
122 QT_BUILD_KEY,
123 key.isEmpty() ? "<null>" : (const char *) key );
124 } else {
125 return TRUE;
126 }
127 return FALSE;
128}
129
130struct qt_token_info
131{
132 qt_token_info( const char *f, const ulong fc )
133 : fields( f ), field_count( fc ), results( fc ), lengths( fc )
134 {
135 results.fill( 0 );
136 lengths.fill( 0 );
137 }
138
139 const char *fields;
140 const ulong field_count;
141
142 QMemArray<const char *> results;
143 QMemArray<ulong> lengths;
144};
145
146/*
147 return values:
148 1 parse ok
149 0 eos
150 -1 parse error
151*/
152static int qt_tokenize( const char *s, ulong s_len, ulong *advance,
153 const qt_token_info &token_info )
154{
155 ulong pos = 0, field = 0, fieldlen = 0;
156 char current;
157 int ret = -1;
158 *advance = 0;
159 for (;;) {
160 current = s[ pos ];
161
162 // next char
163 ++pos;
164 ++fieldlen;
165 ++*advance;
166
167 if ( ! current || pos == s_len + 1 ) {
168 // save result
169 token_info.results[ (int)field ] = s;
170 token_info.lengths[ (int)field ] = fieldlen - 1;
171
172 // end of string
173 ret = 0;
174 break;
175 }
176
177 if ( current == token_info.fields[ field ] ) {
178 // save result
179 token_info.results[ (int)field ] = s;
180 token_info.lengths[ (int)field ] = fieldlen - 1;
181
182 // end of field
183 fieldlen = 0;
184 ++field;
185 if ( field == token_info.field_count - 1 ) {
186 // parse ok
187 ret = 1;
188 }
189 if ( field == token_info.field_count ) {
190 // done parsing
191 break;
192 }
193
194 // reset string and its length
195 s = s + pos;
196 s_len -= pos;
197 pos = 0;
198 }
199 }
200
201 return ret;
202}
203
204/*
205 returns TRUE if the string s was correctly parsed, FALSE otherwise.
206*/
207static bool qt_parse_pattern( const char *s, uint *version, uint *flags,
208 QCString *key )
209{
210 bool ret = TRUE;
211
212 qt_token_info pinfo("=\n", 2);
213 int parse;
214 ulong at = 0, advance, parselen = qstrlen( s );
215 do {
216 parse = qt_tokenize( s + at, parselen, &advance, pinfo );
217 if ( parse == -1 ) {
218 ret = FALSE;
219 break;
220 }
221
222 at += advance;
223 parselen -= advance;
224
225 if ( qstrncmp( "version", pinfo.results[ 0 ], pinfo.lengths[ 0 ] ) == 0 ) {
226 // parse version string
227 qt_token_info pinfo2("..-", 3);
228 if ( qt_tokenize( pinfo.results[ 1 ], pinfo.lengths[ 1 ],
229 &advance, pinfo2 ) != -1 ) {
230 QCString m( pinfo2.results[ 0 ], pinfo2.lengths[ 0 ] + 1 );
231 QCString n( pinfo2.results[ 1 ], pinfo2.lengths[ 1 ] + 1 );
232 QCString p( pinfo2.results[ 2 ], pinfo2.lengths[ 2 ] + 1 );
233 *version = (m.toUInt() << 16) | (n.toUInt() << 8) | p.toUInt();
234 } else {
235 ret = FALSE;
236 break;
237 }
238 } else if ( qstrncmp( "flags", pinfo.results[ 0 ], pinfo.lengths[ 0 ] ) == 0 ) {
239 // parse flags string
240 char ch;
241 *flags = 0;
242 ulong p = 0, c = 0, bit = 0;
243 while ( p < pinfo.lengths[ 1 ] ) {
244 ch = pinfo.results[ 1 ][ p ];
245 bit = pinfo.lengths[ 1 ] - p - 1;
246 c = 1 << bit;
247 if ( ch == '1' ) {
248 *flags |= c;
249 } else if ( ch != '0' ) {
250 ret = FALSE;
251 break;
252 }
253 ++p;
254 }
255 } else if ( qstrncmp( "buildkey", pinfo.results[ 0 ],
256 pinfo.lengths[ 0 ] ) == 0 ){
257 // save buildkey
258 *key = QCString( pinfo.results[ 1 ], pinfo.lengths[ 1 ] + 1 );
259 }
260 } while ( parse == 1 && parselen > 0 );
261
262 return ret;
263}
264
265#if defined(Q_OS_UNIX)
266
267#if defined(Q_OS_FREEBSD) || defined(Q_OS_LINUX)
268# define USE_MMAP
269# include <sys/types.h>
270# include <sys/mman.h>
271#endif // Q_OS_FREEBSD || Q_OS_LINUX
272
273static long qt_find_pattern( const char *s, ulong s_len,
274 const char *pattern, ulong p_len )
275{
276 /*
277 this uses the same algorithm as QString::findRev...
278
279 we search from the end of the file because on the supported
280 systems, the read-only data/text segments are placed at the end
281 of the file. HOWEVER, when building with debugging enabled, all
282 the debug symbols are placed AFTER the data/text segments.
283
284 what does this mean? when building in release mode, the search
285 is fast because the data we are looking for is at the end of the
286 file... when building in debug mode, the search is slower
287 because we have to skip over all the debugging symbols first
288 */
289
290 if ( ! s || ! pattern || p_len > s_len ) return -1;
291 ulong i, hs = 0, hp = 0, delta = s_len - p_len;
292
293 for (i = 0; i < p_len; ++i ) {
294 hs += s[delta + i];
295 hp += pattern[i];
296 }
297 i = delta;
298 for (;;) {
299 if ( hs == hp && qstrncmp( s + i, pattern, p_len ) == 0 )
300 return i;
301 if ( i == 0 )
302 break;
303 --i;
304 hs -= s[i + p_len];
305 hs += s[i];
306 }
307
308 return -1;
309}
310
311/*
312 This opens the specified library, mmaps it into memory, and searches
313 for the QT_UCM_VERIFICATION_DATA. The advantage of this approach is that
314 we can get the verification data without have to actually load the library.
315 This lets us detect mismatches more safely.
316
317 Returns FALSE if version/flags/key information is not present, or if the
318 information could not be read.
319 Returns TRUE if version/flags/key information is present and succesfully read.
320*/
321static bool qt_unix_query( const QString &library, uint *version, uint *flags,
322 QCString *key )
323{
324 QFile file( library );
325 if (! file.open( IO_ReadOnly ) ) {
326 qWarning( "%s: %s", (const char*) QFile::encodeName(library),
327 strerror( errno ) );
328 return FALSE;
329 }
330
331 QByteArray data;
332 char *filedata = 0;
333 ulong fdlen = 0;
334
335#ifdef USE_MMAP
336 char *mapaddr = 0;
337 size_t maplen = file.size();
338 mapaddr = (char *) mmap( mapaddr, maplen, PROT_READ, MAP_PRIVATE, file.handle(), 0 );
339 if ( mapaddr != MAP_FAILED ) {
340 // mmap succeeded
341 filedata = mapaddr;
342 fdlen = maplen;
343 } else {
344 // mmap failed
345 qWarning( "mmap: %s", strerror( errno ) );
346#endif // USE_MMAP
347 // try reading the data into memory instead
348 data = file.readAll();
349 filedata = data.data();
350 fdlen = data.size();
351#ifdef USE_MMAP
352 }
353#endif // USE_MMAP
354
355 // verify that the pattern is present in the plugin
356 const char *pattern = "pattern=QT_UCM_VERIFICATION_DATA";
357 const ulong plen = qstrlen( pattern );
358 long pos = qt_find_pattern( filedata, fdlen, pattern, plen );
359
360 bool ret = FALSE;
361 if ( pos >= 0 ) {
362 ret = qt_parse_pattern( filedata + pos, version, flags, key );
363 }
364
365#ifdef USE_MMAP
366 if ( mapaddr != MAP_FAILED && munmap(mapaddr, maplen) != 0 ) {
367 qWarning( "munmap: %s", strerror( errno ) );
368 }
369#endif // USE_MMAP
370
371 file.close();
372 return ret;
373}
374
375#endif // Q_OS_UNIX
376
377
378static QSettings *cache = 0;
379static QSingleCleanupHandler<QSettings> cleanup_cache;
380
381void QComLibrary::createInstanceInternal()
382{
383 if ( library().isEmpty() )
384 return;
385
386 QFileInfo fileinfo( library() );
387 QString lastModified = fileinfo.lastModified().toString(Qt::ISODate);
388 QString regkey = QString("/Qt Plugins %1.%2/%3")
389 .arg( ( QT_VERSION & 0xff0000 ) >> 16 )
390 .arg( ( QT_VERSION & 0xff00 ) >> 8 )
391 .arg( library() );
392 QStringList reg;
393 uint flags = 0;
394 QCString key;
395 bool query_done = FALSE;
396 bool warn_mismatch = TRUE;
397
398#ifdef QT_THREAD_SUPPORT
399 QMutexLocker locker( qt_global_mutexpool ?
400 qt_global_mutexpool->get( &cache ) : 0 );
401#endif // QT_THREAD_SUPPORT
402
403 if ( ! cache ) {
404 cache = new QSettings;
405 cache->insertSearchPath( QSettings::Windows, "/Trolltech" );
406 cleanup_cache.set( &cache );
407 }
408
409 reg = cache->readListEntry( regkey );
410 if ( reg.count() == 4 ) {
411 // check timestamp
412 if ( lastModified == reg[3] ) {
413 qt_version = reg[0].toUInt(0, 16);
414 flags = reg[1].toUInt(0, 16);
415 key = reg[2].latin1();
416
417 query_done = TRUE;
418 warn_mismatch = FALSE;
419 }
420 }
421
422#if defined(Q_OS_UNIX)
423 if ( ! query_done ) {
424 // get the query information directly from the plugin without loading
425 if ( qt_unix_query( library(), &qt_version, &flags, &key ) ) {
426 // info read succesfully from library
427 query_done = TRUE;
428 }
429 }
430#else // !Q_OS_UNIX
431 if ( ! query_done ) {
432 // get the query information by loading the plugin
433 if ( !isLoaded() ) {
434 Q_ASSERT( entry == 0 );
435 if ( !load() )
436 return;
437 }
438
439# ifdef Q_CC_BOR
440 typedef const char * __stdcall (*UCMQueryVerificationDataProc)();
441# else
442 typedef const char * (*UCMQueryVerificationDataProc)();
443# endif
444 UCMQueryVerificationDataProc ucmQueryVerificationdataProc;
445 ucmQueryVerificationdataProc =
446 (UCMQueryVerificationDataProc) resolve( "qt_ucm_query_verification_data" );
447
448 if ( !ucmQueryVerificationdataProc ||
449 !qt_parse_pattern( ucmQueryVerificationdataProc(),
450 &qt_version, &flags, &key ) ) {
451 qt_version = flags = 0;
452 key = "unknown";
453 } else {
454 query_done = TRUE;
455 }
456 }
457#endif // Q_OS_UNIX
458
459 QStringList queried;
460 queried << QString::number( qt_version,16 )
461 << QString::number( flags, 16 )
462 << key
463 << lastModified;
464
465 if ( queried != reg ) {
466 cache->writeEntry( regkey, queried );
467 // delete the cache, which forces the settings to be written
468 delete cache;
469 cache = 0;
470 }
471
472 if ( ! query_done ) {
473 if ( warn_mismatch ) {
474 qWarning( "Conflict in %s:\n Plugin cannot be queried successfully!",
475 (const char*) QFile::encodeName( library() ) );
476 }
477 unload();
478 return;
479 }
480
481 if ( ! qt_verify( library(), qt_version, flags, key, warn_mismatch ) ) {
482 unload();
483 return;
484 } else if ( !isLoaded() ) {
485 Q_ASSERT( entry == 0 );
486 if ( !load() )
487 return;
488 }
489
490#ifdef Q_CC_BOR
491 typedef QUnknownInterface* __stdcall (*UCMInstanceProc)();
492#else
493 typedef QUnknownInterface* (*UCMInstanceProc)();
494#endif
495 UCMInstanceProc ucmInstanceProc;
496 ucmInstanceProc = (UCMInstanceProc) resolve( "ucm_instantiate" );
497#if defined(QT_DEBUG_COMPONENT)
498 if ( !ucmInstanceProc )
499 qWarning( "%s: Not a UCOM library.", (const char*) QFile::encodeName(library()) );
500#endif
501 entry = ucmInstanceProc ? ucmInstanceProc() : 0;
502
503 if ( entry ) {
504 if ( entry->queryInterface( IID_QLibrary, (QUnknownInterface**)&libiface ) == QS_OK ) {
505 if ( libiface && !libiface->init() ) {
506 libiface->release();
507 libiface = 0;
508 unload();
509 return;
510 }
511 }
512 } else {
513#if defined(QT_DEBUG_COMPONENT)
514 qWarning( "%s: No exported component provided.", (const char*) QFile::encodeName(library()) );
515#endif
516 unload();
517 }
518}
519
520QRESULT QComLibrary::queryInterface( const QUuid& request, QUnknownInterface** iface )
521{
522 if ( !entry )
523 createInstanceInternal();
524 return entry ? entry->queryInterface( request, iface ) : QE_NOCOMPONENT;
525}
526
527uint QComLibrary::qtVersion()
528{
529 if ( !entry )
530 createInstanceInternal();
531 return entry ? qt_version : 0;
532}
533
534
535#endif // QT_NO_COMPONENT
Note: See TracBrowser for help on using the repository browser.