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
delete KSycoca::_self;
00198 }
00199
00200 KSycoca * KSycoca::self()
00201 {
00202
if (!_self) {
00203 qAddPostRoutine(delete_ksycoca_self);
00204 _self =
new KSycoca();
00205 }
00206
return _self;
00207 }
00208
00209 KSycoca::~KSycoca()
00210 {
00211 closeDatabase();
00212
delete d;
00213 _self = 0L;
00214 }
00215
00216
void KSycoca::closeDatabase()
00217 {
00218
QIODevice *device = 0;
00219
if (m_str)
00220 device = m_str->device();
00221
#ifdef HAVE_MMAP
00222
if (device && m_sycoca_mmap)
00223 {
00224
QBuffer *buf = (
QBuffer *) device;
00225 buf->
buffer().resetRawData(m_sycoca_mmap, m_sycoca_size);
00226
00227
00228 munmap((
char*) m_sycoca_mmap, m_sycoca_size);
00229 m_sycoca_mmap = 0;
00230 }
00231
#endif
00232
00233
delete m_str;
00234 m_str = 0;
00235
delete device;
00236
if (d->database != device)
00237
delete d->database;
00238 device = 0;
00239 d->database = 0;
00240
00241
00242
delete m_lstFactories;
00243 m_lstFactories = 0L;
00244 }
00245
00246
void KSycoca::addFactory( KSycocaFactory *factory )
00247 {
00248 assert(m_lstFactories);
00249 m_lstFactories->append(factory);
00250 }
00251
00252
bool KSycoca::isChanged(
const char *type)
00253 {
00254
return self()->d->changeList.contains(type);
00255 }
00256
00257
void KSycoca::notifyDatabaseChanged(
const QStringList &changeList)
00258 {
00259 d->changeList = changeList;
00260
00261
00262
00263
00264
00265 closeDatabase();
00266
00267
00268 emit databaseChanged();
00269 }
00270
00271
QDataStream * KSycoca::findEntry(
int offset, KSycocaType &type)
00272 {
00273
if ( !m_str )
00274 openDatabase();
00275
00276 m_str->device()->at(offset);
00277 Q_INT32 aType;
00278 (*m_str) >> aType;
00279 type = (KSycocaType) aType;
00280
00281
return m_str;
00282 }
00283
00284
bool KSycoca::checkVersion(
bool abortOnError)
00285 {
00286
if ( !m_str )
00287 {
00288
if( !openDatabase(
false ) )
00289
return false;
00290
00291
00292 assert(m_str);
00293 }
00294 m_str->device()->at(0);
00295 Q_INT32 aVersion;
00296 (*m_str) >> aVersion;
00297
if ( aVersion < KSYCOCA_VERSION )
00298 {
00299 kdWarning(7011) <<
"Found version " << aVersion <<
", expecting version " << KSYCOCA_VERSION <<
" or higher." <<
endl;
00300
if (!abortOnError)
return false;
00301 kdError(7011) <<
"Outdated database ! Stop kded and restart it !" <<
endl;
00302 abort();
00303 }
00304
return true;
00305 }
00306
00307
QDataStream * KSycoca::findFactory(KSycocaFactoryId
id)
00308 {
00309
00310
if (bNoDatabase)
00311 {
00312 closeDatabase();
00313
00314
if ( !openDatabase(
false ) )
00315 {
00316
static bool triedLaunchingKdeinit =
false;
00317
if (!triedLaunchingKdeinit)
00318 {
00319 triedLaunchingKdeinit =
true;
00320 kdDebug(7011) <<
"findFactory: we have no database.... launching kdeinit" <<
endl;
00321 KApplication::startKdeinit();
00322
00323 }
00324
if (!openDatabase(
false))
00325
return 0L;
00326 }
00327 }
00328
00329
if (!checkVersion(
false))
00330 {
00331 kdWarning(7011) <<
"Outdated database found" <<
endl;
00332
return 0L;
00333 }
00334 Q_INT32 aId;
00335 Q_INT32 aOffset;
00336
while(
true)
00337 {
00338 (*m_str) >> aId;
00339
00340
if (aId == 0)
00341 {
00342 kdError(7011) <<
"Error, KSycocaFactory (id = " << int(
id) <<
") not found!" <<
endl;
00343
break;
00344 }
00345 (*m_str) >> aOffset;
00346
if (aId ==
id)
00347 {
00348
00349 m_str->device()->at(aOffset);
00350
return m_str;
00351 }
00352 }
00353
return 0;
00354 }
00355
00356
QString KSycoca::kfsstnd_prefixes()
00357 {
00358
if (bNoDatabase)
return "";
00359
if (!checkVersion(
false))
return "";
00360 Q_INT32 aId;
00361 Q_INT32 aOffset;
00362
00363
while(
true)
00364 {
00365 (*m_str) >> aId;
00366
if ( aId )
00367 (*m_str) >> aOffset;
00368
else
00369
break;
00370 }
00371
00372
QString prefixes;
00373
KSycocaEntry::read(*m_str, prefixes);
00374 (*m_str) >> m_timeStamp;
00375
KSycocaEntry::read(*m_str, d->language);
00376 (*m_str) >> d->updateSig;
00377
KSycocaEntry::read(*m_str, d->allResourceDirs);
00378
return prefixes;
00379 }
00380
00381 Q_UINT32 KSycoca::timeStamp()
00382 {
00383
if (!m_timeStamp)
00384 (
void) kfsstnd_prefixes();
00385
return m_timeStamp;
00386 }
00387
00388 Q_UINT32 KSycoca::updateSignature()
00389 {
00390
if (!m_timeStamp)
00391 (
void) kfsstnd_prefixes();
00392
return d->updateSig;
00393 }
00394
00395
QString KSycoca::language()
00396 {
00397
if (d->language.isEmpty())
00398 (
void) kfsstnd_prefixes();
00399
return d->language;
00400 }
00401
00402
QStringList KSycoca::allResourceDirs()
00403 {
00404
if (!m_timeStamp)
00405 (
void) kfsstnd_prefixes();
00406
return d->allResourceDirs;
00407 }
00408
00409
QString KSycoca::determineRelativePath(
const QString & _fullpath,
const char *_resource )
00410 {
00411
QString sRelativeFilePath;
00412
QStringList dirs =
KGlobal::dirs()->
resourceDirs( _resource );
00413 QStringList::ConstIterator dirsit = dirs.begin();
00414
for ( ; dirsit != dirs.end() && sRelativeFilePath.
isEmpty(); ++dirsit ) {
00415
00416
if ( _fullpath.
find( *dirsit ) == 0 )
00417 sRelativeFilePath = _fullpath.
mid( (*dirsit).length() );
00418 }
00419
if ( sRelativeFilePath.
isEmpty() )
00420 kdFatal(7011) <<
QString(
"Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) <<
endl;
00421
00422
00423
00424
return sRelativeFilePath;
00425 }
00426
00427 KSycoca * KSycoca::_self = 0L;
00428
00429
void KSycoca::flagError()
00430 {
00431 qWarning(
"ERROR: KSycoca database corruption!");
00432
if (_self)
00433 {
00434
if (_self->d->readError)
00435
return;
00436 _self->d->readError =
true;
00437
if (_self->d->autoRebuild)
00438 system(
"kbuildsycoca");
00439 }
00440 }
00441
00442
void KSycoca::disableAutoRebuild()
00443 {
00444 d->autoRebuild =
false;
00445 }
00446
00447
bool KSycoca::readError()
00448 {
00449
bool b =
false;
00450
if (_self)
00451 {
00452 b = _self->d->readError;
00453 _self->d->readError =
false;
00454 }
00455
return b;
00456 }
00457
00458 void KSycocaEntry::read(
QDataStream &s,
QString &str )
00459 {
00460 Q_UINT32 bytes;
00461 s >> bytes;
00462
if ( bytes > 8192 ) {
00463
if (bytes != 0xffffffff)
00464 KSycoca::flagError();
00465 str = QString::null;
00466 }
00467
else if ( bytes > 0 ) {
00468
int bt = bytes/2;
00469 str.
setLength( bt );
00470
QChar* ch = (
QChar *) str.
unicode();
00471
char t[8192];
00472
char *b = t;
00473 s.
readRawBytes( b, bytes );
00474
while ( bt-- ) {
00475 *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1];
00476 b += 2;
00477 }
00478 }
else {
00479 str =
"";
00480 }
00481 }
00482
00483
void KSycocaEntry::read(
QDataStream &s,
QStringList &list )
00484 {
00485 list.clear();
00486 Q_UINT32 count;
00487 s >> count;
00488
if (count >= 1024)
00489 {
00490 KSycoca::flagError();
00491
return;
00492 }
00493
for(Q_UINT32 i = 0; i < count; i++)
00494 {
00495
QString str;
00496
read(s, str);
00497 list.append( str );
00498
if (s.
atEnd())
00499 {
00500 KSycoca::flagError();
00501
return;
00502 }
00503 }
00504 }
00505
00506
void KSycoca::virtual_hook(
int id,
void* data )
00507 {
DCOPObject::virtual_hook(
id, data ); }
00508
00509
void KSycocaEntry::virtual_hook(
int,
void* )
00510 { }
00511
00512
#include "ksycoca.moc"