00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
#include "config.h"
00020
00021
#include "ksycoca.h"
00022
#include "ksycocatype.h"
00023
#include "ksycocafactory.h"
00024
00025
#include <qdatastream.h>
00026
#include <qfile.h>
00027
#include <qbuffer.h>
00028
00029
#include <kapplication.h>
00030
#include <dcopclient.h>
00031
#include <kglobal.h>
00032
#include <kdebug.h>
00033
#include <kprocess.h>
00034
#include <kstandarddirs.h>
00035
00036
#include <assert.h>
00037
#include <stdlib.h>
00038
#include <unistd.h>
00039
#include <fcntl.h>
00040
00041
#ifdef HAVE_SYS_MMAN_H
00042
#include <sys/mman.h>
00043
#endif
00044
00045
#ifndef MAP_FAILED
00046
#define MAP_FAILED ((void *) -1)
00047
#endif
00048
00049
template class QPtrList<KSycocaFactory>;
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
class KSycocaPrivate {
00060
public:
00061 KSycocaPrivate() {
00062 database = 0;
00063 readError =
false;
00064 updateSig = 0;
00065 autoRebuild =
true;
00066 }
00067
QFile *database;
00068
QStringList changeList;
00069
QString language;
00070
bool readError;
00071
bool autoRebuild;
00072 Q_UINT32 updateSig;
00073
QStringList allResourceDirs;
00074 };
00075
00076
int KSycoca::version()
00077 {
00078
return KSYCOCA_VERSION;
00079 }
00080
00081
00082 KSycoca::KSycoca()
00083 :
DCOPObject("ksycoca"), m_lstFactories(0), m_str(0), bNoDatabase(false),
00084 m_sycoca_size(0), m_sycoca_mmap(0), m_timeStamp(0)
00085 {
00086 d =
new KSycocaPrivate;
00087
00088
if (kapp && !kapp->dcopClient()->isAttached())
00089 {
00090 kapp->dcopClient()->attach();
00091 }
00092
00093
00094
00095
00096 openDatabase();
00097 _self =
this;
00098 }
00099
00100
bool KSycoca::openDatabase(
bool openDummyIfNotFound )
00101 {
00102
bool result =
true;
00103
00104 m_sycoca_mmap = 0;
00105 m_str = 0;
00106
QString path;
00107
QCString ksycoca_env = getenv(
"KDESYCOCA");
00108
if (ksycoca_env.isEmpty())
00109 path =
KGlobal::dirs()->
saveLocation(
"cache") +
"ksycoca";
00110
else
00111 path = QFile::decodeName(ksycoca_env);
00112
00113 kdDebug(7011) <<
"Trying to open ksycoca from " << path <<
endl;
00114
QFile *database =
new QFile(path);
00115
bool bOpen = database->open( IO_ReadOnly );
00116
if (!bOpen)
00117 {
00118 path = locate(
"services",
"ksycoca");
00119
if (!path.isEmpty())
00120 {
00121 kdDebug(7011) <<
"Trying to open global ksycoca from " << path <<
endl;
00122
delete database;
00123 database =
new QFile(path);
00124 bOpen = database->open( IO_ReadOnly );
00125 }
00126 }
00127
00128
if (bOpen)
00129 {
00130 fcntl(database->handle(), F_SETFD, FD_CLOEXEC);
00131 m_sycoca_size = database->size();
00132
#ifdef HAVE_MMAP
00133
m_sycoca_mmap = (
const char *) mmap(0, m_sycoca_size,
00134 PROT_READ, MAP_SHARED,
00135 database->handle(), 0);
00136
00137
00138
if (m_sycoca_mmap == (
const char*) MAP_FAILED || m_sycoca_mmap == 0)
00139 {
00140 kdDebug(7011) <<
"mmap failed. (length = " << m_sycoca_size <<
")" <<
endl;
00141
#endif
00142
m_str =
new QDataStream(database);
00143
#ifdef HAVE_MMAP
00144
}
00145
else
00146 {
00147
QByteArray b_array;
00148 b_array.setRawData(m_sycoca_mmap, m_sycoca_size);
00149
QBuffer *buffer =
new QBuffer( b_array );
00150 buffer->open(IO_ReadWrite);
00151 m_str =
new QDataStream( buffer);
00152 }
00153
#endif
00154
bNoDatabase =
false;
00155 }
00156
else
00157 {
00158 kdDebug(7011) <<
"Could not open ksycoca" <<
endl;
00159
00160
00161
delete database;
00162 database = 0;
00163
00164 bNoDatabase =
true;
00165
if (openDummyIfNotFound)
00166 {
00167
00168
00169
QBuffer *buffer =
new QBuffer(
QByteArray() );
00170 buffer->open(IO_ReadWrite);
00171 m_str =
new QDataStream( buffer);
00172 (*m_str) << (Q_INT32) KSYCOCA_VERSION;
00173 (*m_str) << (Q_INT32) 0;
00174 }
00175
else
00176 {
00177 result =
false;
00178 }
00179 }
00180 m_lstFactories =
new KSycocaFactoryList();
00181 m_lstFactories->setAutoDelete(
true );
00182 d->database = database;
00183
return result;
00184 }
00185
00186
00187 KSycoca::KSycoca(
bool )
00188 :
DCOPObject("ksycoca_building"), m_lstFactories(0), m_str(0), bNoDatabase(false),
00189 m_sycoca_size(0), m_sycoca_mmap(0)
00190 {
00191 d =
new KSycocaPrivate;
00192 m_lstFactories =
new KSycocaFactoryList();
00193 m_lstFactories->setAutoDelete(
true );
00194 _self =
this;
00195 }
00196
00197
static void delete_ksycoca_self() {
00198
delete KSycoca::_self;
00199 }
00200
00201 KSycoca * KSycoca::self()
00202 {
00203
if (!_self) {
00204 qAddPostRoutine(delete_ksycoca_self);
00205 _self =
new KSycoca();
00206 }
00207
return _self;
00208 }
00209
00210 KSycoca::~KSycoca()
00211 {
00212 closeDatabase();
00213
delete d;
00214 _self = 0L;
00215 }
00216
00217
void KSycoca::closeDatabase()
00218 {
00219
QIODevice *device = 0;
00220
if (m_str)
00221 device = m_str->device();
00222
#ifdef HAVE_MMAP
00223
if (device && m_sycoca_mmap)
00224 {
00225
QBuffer *buf = (
QBuffer *) device;
00226 buf->buffer().resetRawData(m_sycoca_mmap, m_sycoca_size);
00227
00228
00229 munmap((
char*) m_sycoca_mmap, m_sycoca_size);
00230 m_sycoca_mmap = 0;
00231 }
00232
#endif
00233
00234
delete m_str;
00235 m_str = 0;
00236
delete device;
00237
if (d->database != device)
00238
delete d->database;
00239 device = 0;
00240 d->database = 0;
00241
00242
00243
delete m_lstFactories;
00244 m_lstFactories = 0L;
00245 }
00246
00247
void KSycoca::addFactory( KSycocaFactory *factory )
00248 {
00249 assert(m_lstFactories);
00250 m_lstFactories->append(factory);
00251 }
00252
00253
bool KSycoca::isChanged(
const char *type)
00254 {
00255
return self()->d->changeList.contains(type);
00256 }
00257
00258
void KSycoca::notifyDatabaseChanged(
const QStringList &changeList)
00259 {
00260 d->changeList = changeList;
00261
00262
00263
00264
00265
00266 closeDatabase();
00267
00268
00269 emit databaseChanged();
00270 }
00271
00272
QDataStream * KSycoca::findEntry(
int offset, KSycocaType &type)
00273 {
00274
if ( !m_str )
00275 openDatabase();
00276
00277 m_str->device()->at(offset);
00278 Q_INT32 aType;
00279 (*m_str) >> aType;
00280 type = (KSycocaType) aType;
00281
00282
return m_str;
00283 }
00284
00285
bool KSycoca::checkVersion(
bool abortOnError)
00286 {
00287
if ( !m_str )
00288 {
00289
if( !openDatabase(
false ) )
00290
return false;
00291
00292
00293 assert(m_str);
00294 }
00295 m_str->device()->at(0);
00296 Q_INT32 aVersion;
00297 (*m_str) >> aVersion;
00298
if ( aVersion < KSYCOCA_VERSION )
00299 {
00300 kdWarning(7011) <<
"Found version " << aVersion <<
", expecting version " << KSYCOCA_VERSION <<
" or higher." <<
endl;
00301
if (!abortOnError)
return false;
00302 kdError(7011) <<
"Outdated database ! Stop kded and restart it !" <<
endl;
00303 abort();
00304 }
00305
return true;
00306 }
00307
00308
QDataStream * KSycoca::findFactory(KSycocaFactoryId
id)
00309 {
00310
00311
if (bNoDatabase)
00312 {
00313 closeDatabase();
00314
00315
if ( !openDatabase(
false ) )
00316 {
00317
static bool triedLaunchingKdeinit =
false;
00318
if (!triedLaunchingKdeinit)
00319 {
00320 triedLaunchingKdeinit =
true;
00321 kdDebug(7011) <<
"findFactory: we have no database.... launching kdeinit" <<
endl;
00322 KApplication::startKdeinit();
00323
00324 }
00325
if (!openDatabase(
false))
00326
return 0L;
00327 }
00328 }
00329
00330
if (!checkVersion(
false))
00331 {
00332 kdWarning(7011) <<
"Outdated database found" <<
endl;
00333
return 0L;
00334 }
00335 Q_INT32 aId;
00336 Q_INT32 aOffset;
00337
while(
true)
00338 {
00339 (*m_str) >> aId;
00340
00341
if (aId == 0)
00342 {
00343 kdError(7011) <<
"Error, KSycocaFactory (id = " << int(
id) <<
") not found!" <<
endl;
00344
break;
00345 }
00346 (*m_str) >> aOffset;
00347
if (aId ==
id)
00348 {
00349
00350 m_str->device()->at(aOffset);
00351
return m_str;
00352 }
00353 }
00354
return 0;
00355 }
00356
00357
QString KSycoca::kfsstnd_prefixes()
00358 {
00359
if (bNoDatabase)
return "";
00360
if (!checkVersion(
false))
return "";
00361 Q_INT32 aId;
00362 Q_INT32 aOffset;
00363
00364
while(
true)
00365 {
00366 (*m_str) >> aId;
00367
if ( aId )
00368 (*m_str) >> aOffset;
00369
else
00370
break;
00371 }
00372
00373
QString prefixes;
00374
KSycocaEntry::read(*m_str, prefixes);
00375 (*m_str) >> m_timeStamp;
00376
KSycocaEntry::read(*m_str, d->language);
00377 (*m_str) >> d->updateSig;
00378
KSycocaEntry::read(*m_str, d->allResourceDirs);
00379
return prefixes;
00380 }
00381
00382 Q_UINT32 KSycoca::timeStamp()
00383 {
00384
if (!m_timeStamp)
00385 (
void) kfsstnd_prefixes();
00386
return m_timeStamp;
00387 }
00388
00389 Q_UINT32 KSycoca::updateSignature()
00390 {
00391
if (!m_timeStamp)
00392 (
void) kfsstnd_prefixes();
00393
return d->updateSig;
00394 }
00395
00396
QString KSycoca::language()
00397 {
00398
if (d->language.isEmpty())
00399 (
void) kfsstnd_prefixes();
00400
return d->language;
00401 }
00402
00403
QStringList KSycoca::allResourceDirs()
00404 {
00405
if (!m_timeStamp)
00406 (
void) kfsstnd_prefixes();
00407
return d->allResourceDirs;
00408 }
00409
00410
QString KSycoca::determineRelativePath(
const QString & _fullpath,
const char *_resource )
00411 {
00412
QString sRelativeFilePath;
00413
QStringList dirs =
KGlobal::dirs()->
resourceDirs( _resource );
00414 QStringList::ConstIterator dirsit = dirs.begin();
00415
for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) {
00416
00417
if ( _fullpath.find( *dirsit ) == 0 )
00418 sRelativeFilePath = _fullpath.mid( (*dirsit).length() );
00419 }
00420
if ( sRelativeFilePath.isEmpty() )
00421 kdFatal(7011) <<
QString(
"Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) <<
endl;
00422
00423
00424
00425
return sRelativeFilePath;
00426 }
00427
00428 KSycoca * KSycoca::_self = 0L;
00429
00430
void KSycoca::flagError()
00431 {
00432 qWarning(
"ERROR: KSycoca database corruption!");
00433
if (_self)
00434 {
00435
if (_self->d->readError)
00436
return;
00437 _self->d->readError =
true;
00438
if (_self->d->autoRebuild)
00439 system(
"kbuildsycoca");
00440 }
00441 }
00442
00443
void KSycoca::disableAutoRebuild()
00444 {
00445 d->autoRebuild =
false;
00446 }
00447
00448
bool KSycoca::readError()
00449 {
00450
bool b =
false;
00451
if (_self)
00452 {
00453 b = _self->d->readError;
00454 _self->d->readError =
false;
00455 }
00456
return b;
00457 }
00458
00459 void KSycocaEntry::read(
QDataStream &s,
QString &str )
00460 {
00461 Q_UINT32 bytes;
00462 s >> bytes;
00463
if ( bytes > 8192 ) {
00464
if (bytes != 0xffffffff)
00465 KSycoca::flagError();
00466 str = QString::null;
00467 }
00468
else if ( bytes > 0 ) {
00469
int bt = bytes/2;
00470 str.setLength( bt );
00471
QChar* ch = (
QChar *) str.unicode();
00472
char t[8192];
00473
char *b = t;
00474 s.readRawBytes( b, bytes );
00475
while ( bt-- ) {
00476 *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1];
00477 b += 2;
00478 }
00479 }
else {
00480 str =
"";
00481 }
00482 }
00483
00484
void KSycocaEntry::read(
QDataStream &s,
QStringList &list )
00485 {
00486 list.clear();
00487 Q_UINT32 count;
00488 s >> count;
00489
if (count >= 1024)
00490 {
00491 KSycoca::flagError();
00492
return;
00493 }
00494
for(Q_UINT32 i = 0; i < count; i++)
00495 {
00496
QString str;
00497
read(s, str);
00498 list.append( str );
00499
if (s.atEnd())
00500 {
00501 KSycoca::flagError();
00502
return;
00503 }
00504 }
00505 }
00506
00507
void KSycoca::virtual_hook(
int id,
void* data )
00508 {
DCOPObject::virtual_hook(
id, data ); }
00509
00510
void KSycocaEntry::virtual_hook(
int,
void* )
00511 { }
00512
00513
#include "ksycoca.moc"