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