summaryrefslogtreecommitdiffabout
path: root/microkde/kdecore/klibloader.cpp
Unidiff
Diffstat (limited to 'microkde/kdecore/klibloader.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--microkde/kdecore/klibloader.cpp626
1 files changed, 626 insertions, 0 deletions
diff --git a/microkde/kdecore/klibloader.cpp b/microkde/kdecore/klibloader.cpp
new file mode 100644
index 0000000..1410308
--- a/dev/null
+++ b/microkde/kdecore/klibloader.cpp
@@ -0,0 +1,626 @@
1/* This file is part of the KDE libraries
2 Copyright (C) 1999 Torben Weis <weis@kde.org>
3 Copyright (C) 2000 Michael Matz <matz@kde.org>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA.
18*/
19//US #include <config.h>
20#include <qclipboard.h>
21#include <qfile.h>
22#include <qtimer.h>
23#include <qobjectdict.h>
24#include <qwidgetlist.h>
25#include <qwidget.h>
26
27#include "kapplication.h"
28#include "klibloader.h"
29#include "kstandarddirs.h"
30#include "kdebug.h"
31#include "klocale.h"
32
33//US #include "ltdl.h"
34
35//US do everything through qlibrary
36#ifndef DESKTOP_VERSION
37#include <qpe/qpeapplication.h>
38#include <qtopia/qlibrary.h>
39#endif
40
41/*US
42#ifdef Q_WS_X11
43#include <X11/Xlib.h>
44#include <X11/Xatom.h>
45#endif
46*/
47template class QAsciiDict<KLibrary>;
48
49#include <stdlib.h> //getenv
50
51/*US
52#if HAVE_DLFCN_H
53# include <dlfcn.h>
54#endif
55
56#ifdef RTLD_GLOBAL
57# define LT_GLOBAL RTLD_GLOBAL
58#else
59# ifdef DL_GLOBAL
60# define LT_GLOBAL DL_GLOBAL
61# endif
62#endif
63#ifndef LT_GLOBAL
64# define LT_GLOBAL 0
65#endif
66*/
67
68/*US
69extern "C" {
70extern int lt_dlopen_flag;
71}
72*/
73
74KLibFactory::KLibFactory( QObject* parent, const char* name )
75 : QObject( parent, name )
76{
77}
78
79KLibFactory::~KLibFactory()
80{
81// kdDebug(150) << "Deleting KLibFactory " << this << endl;
82}
83
84QObject* KLibFactory::create( QObject* parent, const char* name, const char* classname, const QStringList &args )
85{
86 QObject* obj = createObject( parent, name, classname, args );
87 if ( obj )
88 emit objectCreated( obj );
89 return obj;
90}
91
92
93QObject* KLibFactory::createObject( QObject*, const char*, const char*, const QStringList &)
94{
95 return 0;
96}
97
98
99// -----------------------------------------------
100
101//US KLibrary::KLibrary( const QString& libname, const QString& filename, void * handle )
102KLibrary::KLibrary( const QString& libname, const QString& filename, QLibrary* handle )
103{
104 /* Make sure, we have a KLibLoader */
105 (void) KLibLoader::self();
106 m_libname = libname;
107 m_filename = filename;
108 m_handle = handle;
109 m_factory = 0;
110 m_timer = 0;
111}
112
113KLibrary::~KLibrary()
114{
115// kdDebug(150) << "Deleting KLibrary " << this << " " << m_libname << endl;
116 if ( m_timer && m_timer->isActive() )
117 m_timer->stop();
118
119 // If any object is remaining, delete
120 if ( m_objs.count() > 0 )
121 {
122 QPtrListIterator<QObject> it( m_objs );
123 for ( ; it.current() ; ++it )
124 {
125 kdDebug(150) << "Factory still has object " << it.current() << " " << it.current()->name () << " Library = " << m_libname << endl;
126 disconnect( it.current(), SIGNAL( destroyed() ),
127 this, SLOT( slotObjectDestroyed() ) );
128 }
129 m_objs.setAutoDelete(true);
130 m_objs.clear();
131 }
132
133 if ( m_factory ) {
134 //kdDebug(150) << " ... deleting the factory " << m_factory << endl;
135 delete m_factory;
136 }
137}
138
139QString KLibrary::name() const
140{
141 return m_libname;
142}
143
144QString KLibrary::fileName() const
145{
146 return m_filename;
147}
148
149KLibFactory* KLibrary::factory()
150{
151 if ( m_factory )
152 return m_factory;
153
154 QCString symname;
155 symname.sprintf("init_%s", name().latin1() );
156
157 void* sym = symbol( symname );
158 if ( !sym )
159 {
160 kdWarning(150) << "KLibrary: The library " << name() << " does not offer an init_" << name() << " function" << endl;
161 return 0;
162 }
163
164 typedef KLibFactory* (*t_func)();
165 t_func func = (t_func)sym;
166 m_factory = func();
167
168 if( !m_factory )
169 {
170 kdWarning(150) << "KLibrary: The library " << name() << " does not offer a KDE compatible factory" << endl;
171 return 0;
172 }
173
174 connect( m_factory, SIGNAL( objectCreated( QObject * ) ),
175 this, SLOT( slotObjectCreated( QObject * ) ) );
176
177 return m_factory;
178}
179
180void* KLibrary::symbol( const char* symname ) const
181{
182//US void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname );
183 void* sym = m_handle->resolve( symname );
184 if ( !sym )
185 {
186//US kdWarning(150) << "KLibrary: " << lt_dlerror() << endl;
187 kdWarning(150) << "KLibrary: " << m_libname << ", symbol:" << symname << " not found " << endl;
188 return 0;
189 }
190
191 return sym;
192}
193
194bool KLibrary::hasSymbol( const char* symname ) const
195{
196//US void* sym = lt_dlsym( (lt_dlhandle) m_handle, symname );
197 void* sym = m_handle->resolve( symname );
198 return (sym != 0L );
199}
200
201void KLibrary::unload() const
202{
203 if (KLibLoader::s_self)
204 KLibLoader::s_self->unloadLibrary(QFile::encodeName(name()));
205}
206
207void KLibrary::slotObjectCreated( QObject *obj )
208{
209 if ( !obj )
210 return;
211
212 if ( m_timer && m_timer->isActive() )
213 m_timer->stop();
214
215 if ( m_objs.containsRef( obj ) )
216 return; // we know this object already
217
218 connect( obj, SIGNAL( destroyed() ),
219 this, SLOT( slotObjectDestroyed() ) );
220
221 m_objs.append( obj );
222}
223
224void KLibrary::slotObjectDestroyed()
225{
226 m_objs.removeRef( sender() );
227
228 if ( m_objs.count() == 0 )
229 {
230// kdDebug(150) << "KLibrary: shutdown timer for " << name() << " started!"
231// << endl;
232
233 if ( !m_timer )
234 {
235 m_timer = new QTimer( this, "klibrary_shutdown_timer" );
236 connect( m_timer, SIGNAL( timeout() ),
237 this, SLOT( slotTimeout() ) );
238 }
239
240 // as long as it's not stable make the timeout short, for debugging
241 // pleasure (matz)
242 //m_timer->start( 1000*60, true );
243 m_timer->start( 1000*10, true );
244 }
245}
246
247void KLibrary::slotTimeout()
248{
249 if ( m_objs.count() != 0 )
250 return;
251
252 /* Don't go through KLibLoader::unloadLibrary(), because that uses the
253 ref counter, but this timeout means to unconditionally close this library
254 The destroyed() signal will take care to remove us from all lists.
255 */
256 delete this;
257}
258
259// -------------------------------------------------
260
261/* This helper class is needed, because KLibraries can go away without
262 being unloaded. So we need some info about KLibraries even after its
263 death. */
264class KLibWrapPrivate
265{
266public:
267//US KLibWrapPrivate(KLibrary *l, lt_dlhandle h);
268 KLibWrapPrivate(KLibrary *l, QLibrary* h);
269
270 KLibrary *lib;
271 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
272 int ref_count;
273//US lt_dlhandle handle;
274 QLibrary *handle;
275 QString name;
276 QString filename;
277};
278
279//US KLibWrapPrivate::KLibWrapPrivate(KLibrary *l, lt_dlhandle h)
280KLibWrapPrivate::KLibWrapPrivate(KLibrary *l, QLibrary* h)
281 : lib(l), ref_count(1), handle(h), name(l->name()), filename(l->fileName())
282{
283 unload_mode = UNKNOWN;
284/*US
285 if (lt_dlsym(handle, "__kde_do_not_unload") != 0) {
286// kdDebug(150) << "Will not unload " << name << endl;
287 unload_mode = DONT_UNLOAD;
288 } else if (lt_dlsym(handle, "__kde_do_unload") != 0) {
289 unload_mode = UNLOAD;
290 }
291*/
292//US use instead:
293 if (h->resolve("__kde_do_not_unload") != 0) {
294// kdDebug(150) << "Will not unload " << name << endl;
295 unload_mode = DONT_UNLOAD;
296 } else if (h->resolve("__kde_do_unload") != 0) {
297 unload_mode = UNLOAD;
298 }
299}
300
301class KLibLoaderPrivate
302{
303public:
304 QPtrList<KLibWrapPrivate> loaded_stack;
305 QPtrList<KLibWrapPrivate> pending_close;
306 enum {UNKNOWN, UNLOAD, DONT_UNLOAD} unload_mode;
307
308 QString errorMessage;
309};
310
311KLibLoader* KLibLoader::s_self = 0;
312
313KLibLoader* KLibLoader::self()
314{
315 if ( !s_self )
316 s_self = new KLibLoader;
317 return s_self;
318}
319
320void KLibLoader::cleanUp()
321{
322 if ( !s_self )
323 return;
324
325 delete s_self;
326 s_self = 0;
327}
328
329KLibLoader::KLibLoader( QObject* parent, const char* name )
330 : QObject( parent, name )
331{
332 s_self = this;
333 d = new KLibLoaderPrivate;
334//US lt_dlinit();
335 d->unload_mode = KLibLoaderPrivate::UNKNOWN;
336 if (getenv("KDE_NOUNLOAD") != 0)
337 d->unload_mode = KLibLoaderPrivate::DONT_UNLOAD;
338 else if (getenv("KDE_DOUNLOAD") != 0)
339 d->unload_mode = KLibLoaderPrivate::UNLOAD;
340 d->loaded_stack.setAutoDelete( true );
341}
342
343KLibLoader::~KLibLoader()
344{
345// kdDebug(150) << "Deleting KLibLoader " << this << " " << name() << endl;
346
347 QAsciiDictIterator<KLibWrapPrivate> it( m_libs );
348 for (; it.current(); ++it )
349 {
350 kdDebug(150) << "The KLibLoader contains the library " << it.current()->name
351 << " (" << it.current()->lib << ")" << endl;
352 d->pending_close.append(it.current());
353 }
354
355 close_pending(0);
356
357 delete d;
358}
359
360//static
361QString KLibLoader::findLibrary( const char * name/*US , const KInstance * instance*/ )
362{
363 QCString libname( name );
364
365 // only append ".la" if there is no extension
366 // this allows to load non-libtool libraries as well
367 // (mhk, 20000228)
368 int pos = libname.findRev('/');
369 if (pos < 0)
370 pos = 0;
371 if (libname.find('.', pos) < 0)
372 libname += ".la";
373
374 // only look up the file if it is not an absolute filename
375 // (mhk, 20000228)
376 QString libfile;
377 if (libname[0] == '/')
378 libfile = libname;
379 else
380 {
381//US libfile = instance->dirs()->findResource( "module", libname );
382 libfile = KGlobal::dirs()->findResource( "module", libname );
383 if ( libfile.isEmpty() )
384 {
385//US libfile = instance->dirs()->findResource( "lib", libname );
386 libfile = KGlobal::dirs()->findResource( "lib", libname );
387#ifndef NDEBUG
388 if ( !libfile.isEmpty() && libname.left(3) == "lib" ) // don't warn for kdeinit modules
389 kdDebug(150) << "library " << libname << " not found under 'module' but under 'lib'" << endl;
390#endif
391 }
392 if ( libfile.isEmpty() )
393 {
394#ifndef NDEBUG
395 kdDebug(150) << "library=" << libname << ": No file names " << libname.data() << " found in paths." << endl;
396#endif
397 self()->d->errorMessage = i18n("Library files for \"%1\" not found in paths").arg(libname);
398 }
399 else
400 self()->d->errorMessage = QString::null;
401 }
402 return libfile;
403}
404
405
406KLibrary* KLibLoader::globalLibrary( const char *name )
407{
408KLibrary *tmp;
409/*US
410int olt_dlopen_flag = lt_dlopen_flag;
411
412 lt_dlopen_flag |= LT_GLOBAL;
413 kdDebug(150) << "Loading the next library global with flag "
414 << lt_dlopen_flag
415 << "." << endl;
416*/
417 tmp = library(name);
418/*US
419 lt_dlopen_flag = olt_dlopen_flag;
420*/
421return tmp;
422}
423
424
425KLibrary* KLibLoader::library( const char *name )
426{
427 if (!name)
428 return 0;
429
430 KLibWrapPrivate* wrap = m_libs[name];
431 if (wrap) {
432 /* Nothing to do to load the library. */
433 wrap->ref_count++;
434 return wrap->lib;
435 }
436
437 /* Test if this library was loaded at some time, but got
438 unloaded meanwhile, whithout being dlclose()'ed. */
439 QPtrListIterator<KLibWrapPrivate> it(d->loaded_stack);
440 for (; it.current(); ++it) {
441 if (it.current()->name == name)
442 wrap = it.current();
443 }
444
445 if (wrap) {
446 d->pending_close.removeRef(wrap);
447 if (!wrap->lib) {
448 /* This lib only was in loaded_stack, but not in m_libs. */
449 wrap->lib = new KLibrary( name, wrap->filename, wrap->handle );
450 }
451 wrap->ref_count++;
452 } else {
453 QString libfile = findLibrary( name );
454 if ( libfile.isEmpty() )
455 return 0;
456
457 const QString & qpeDir = QPEApplication::qpeDir();
458 libfile = qpeDir + libfile;
459//US QLibrary *lib = new QLibrary( qpeDir + "/plugins/korganizer/libopiekabc.so", QLibrary::Immediately );
460 QLibrary *qlib = new QLibrary( libfile.latin1(), QLibrary::Immediately );
461
462//US lt_dlhandle handle = lt_dlopen( libfile.latin1() );
463//US if ( !handle )
464 if ( !qlib )
465 {
466//US const char* errmsg = lt_dlerror();
467 char* errmsg;
468 sprintf(errmsg, "KLibLoader::library could not load library: %s", libfile.latin1());
469 qDebug(errmsg);
470
471 if(errmsg)
472 d->errorMessage = QString::fromLatin1(errmsg);
473 else
474 d->errorMessage = QString::null;
475 kdWarning(150) << "library=" << name << ": file=" << libfile << ": " << d->errorMessage << endl;
476 return 0;
477 }
478 else
479 d->errorMessage = QString::null;
480
481 KLibrary *lib = new KLibrary( name, libfile, qlib );
482 wrap = new KLibWrapPrivate(lib, qlib);
483 d->loaded_stack.prepend(wrap);
484 }
485 m_libs.insert( name, wrap );
486
487 connect( wrap->lib, SIGNAL( destroyed() ),
488 this, SLOT( slotLibraryDestroyed() ) );
489
490 return wrap->lib;
491}
492
493QString KLibLoader::lastErrorMessage() const
494{
495 return d->errorMessage;
496}
497
498void KLibLoader::unloadLibrary( const char *libname )
499{
500 KLibWrapPrivate *wrap = m_libs[ libname ];
501 if (!wrap)
502 return;
503 if (--wrap->ref_count)
504 return;
505
506// kdDebug(150) << "closing library " << libname << endl;
507
508 m_libs.remove( libname );
509
510 disconnect( wrap->lib, SIGNAL( destroyed() ),
511 this, SLOT( slotLibraryDestroyed() ) );
512 close_pending( wrap );
513}
514
515KLibFactory* KLibLoader::factory( const char* name )
516{
517 KLibrary* lib = library( name );
518 if ( !lib )
519 return 0;
520
521 return lib->factory();
522}
523
524void KLibLoader::slotLibraryDestroyed()
525{
526 const KLibrary *lib = static_cast<const KLibrary *>( sender() );
527
528 QAsciiDictIterator<KLibWrapPrivate> it( m_libs );
529 for (; it.current(); ++it )
530 if ( it.current()->lib == lib )
531 {
532 KLibWrapPrivate *wrap = it.current();
533 wrap->lib = 0; /* the KLibrary object is already away */
534 m_libs.remove( it.currentKey() );
535 close_pending( wrap );
536 return;
537 }
538}
539
540void KLibLoader::close_pending(KLibWrapPrivate *wrap)
541{
542 if (wrap && !d->pending_close.containsRef( wrap ))
543 d->pending_close.append( wrap );
544
545 /* First delete all KLibrary objects in pending_close, but _don't_ unload
546 the DSO behind it. */
547 QPtrListIterator<KLibWrapPrivate> it(d->pending_close);
548 for (; it.current(); ++it) {
549 wrap = it.current();
550 if (wrap->lib) {
551 disconnect( wrap->lib, SIGNAL( destroyed() ),
552 this, SLOT( slotLibraryDestroyed() ) );
553 delete wrap->lib;
554 wrap->lib = 0;
555 }
556 }
557
558 if (d->unload_mode == KLibLoaderPrivate::DONT_UNLOAD) return;
559
560 bool deleted_one = false;
561 while ((wrap = d->loaded_stack.first())) {
562 /* Let's first see, if we want to try to unload this lib.
563 If the env. var KDE_DOUNLOAD is set, we try to unload every lib.
564 If not, we look at the lib itself, and unload it only, if it exports
565 the symbol __kde_do_unload. */
566 if (d->unload_mode != KLibLoaderPrivate::UNLOAD
567 && wrap->unload_mode != KLibWrapPrivate::UNLOAD)
568 break;
569
570 /* Now ensure, that the libs are only unloaded in the reverse direction
571 they were loaded. */
572 if (!d->pending_close.containsRef( wrap )) {
573 if (!deleted_one)
574 /* Only diagnose, if we really haven't deleted anything. */
575// kdDebug(150) << "try to dlclose " << wrap->name << ": not yet" << endl;
576 break;
577 }
578
579// kdDebug(150) << "try to dlclose " << wrap->name << ": yes, done." << endl;
580
581#ifndef Q_WS_QWS
582 if ( !deleted_one ) {
583 /* Only do the hack once in this loop.
584 WABA: *HACK*
585 We need to make sure to clear the clipboard before unloading a DSO
586 because the DSO could have defined an object derived from QMimeSource
587 and placed that on the clipboard. */
588 /*kapp->clipboard()->clear();*/
589
590 /* Well.. let's do something more subtle... convert the clipboard context
591 to text. That should be safe as it only uses objects defined by Qt. */
592
593 QWidgetList *widgetlist = QApplication::topLevelWidgets();
594 QWidget *co = widgetlist->first();
595 while (co) {
596 if (qstrcmp(co->name(), "internal clipboard owner") == 0) {
597 if (XGetSelectionOwner(co->x11Display(), XA_PRIMARY) == co->winId())
598 kapp->clipboard()->setText(kapp->clipboard()->text());
599
600 break;
601 }
602 co = widgetlist->next();
603 }
604 delete widgetlist;
605 }
606#else
607 // FIXME(E): Implement in Qt Embedded
608#endif
609
610 deleted_one = true;
611//US lt_dlclose(wrap->handle);
612 wrap->handle->unload();
613
614 d->pending_close.removeRef(wrap);
615 /* loaded_stack is AutoDelete, so wrap is freed */
616 d->loaded_stack.remove();
617 }
618}
619
620void KLibLoader::virtual_hook( int, void* )
621{ /*BASE::virtual_hook( id, data );*/ }
622
623void KLibFactory::virtual_hook( int, void* )
624{ /*BASE::virtual_hook( id, data );*/ }
625
626//US #include "klibloader.moc"