-rw-r--r-- | qmake/tools/qcomlibrary.cpp | 538 |
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 | |||
57 | QComLibrary::QComLibrary( const QString &filename ) | ||
58 | : QLibrary( filename ), entry( 0 ), libiface( 0 ), qt_version( 0 ) | ||
59 | { | ||
60 | } | ||
61 | |||
62 | QComLibrary::~QComLibrary() | ||
63 | { | ||
64 | if ( autoUnload() ) | ||
65 | unload(); | ||
66 | } | ||
67 | |||
68 | bool 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 | |||
86 | static 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 | |||
126 | struct 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 | */ | ||
148 | static 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 | */ | ||
203 | static 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 | |||
269 | static 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 | */ | ||
317 | static 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 | |||
374 | static QSettings *cache = 0; | ||
375 | static QSingleCleanupHandler<QSettings> cleanup_cache; | ||
376 | |||
377 | void 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 | |||
523 | QRESULT QComLibrary::queryInterface( const QUuid& request, QUnknownInterface** iface ) | ||
524 | { | ||
525 | if ( !entry ) | ||
526 | createInstanceInternal(); | ||
527 | return entry ? entry->queryInterface( request, iface ) : QE_NOCOMPONENT; | ||
528 | } | ||
529 | |||
530 | uint QComLibrary::qtVersion() | ||
531 | { | ||
532 | if ( !entry ) | ||
533 | createInstanceInternal(); | ||
534 | return entry ? qt_version : 0; | ||
535 | } | ||
536 | |||
537 | |||
538 | #endif // QT_NO_COMPONENT | ||