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