kpilot Library API Documentation

sysinfo-conduit.cc

00001 /* sysinfo-conduit.cc KPilot 00002 ** 00003 ** Copyright (C) 2003 by Reinhold Kainhofer 00004 ** 00005 */ 00006 00007 /* 00008 ** This program is free software; you can redistribute it and/or modify 00009 ** it under the terms of the GNU General Public License as published by 00010 ** the Free Software Foundation; either version 2 of the License, or 00011 ** (at your option) any later version. 00012 ** 00013 ** This program is distributed in the hope that it will be useful, 00014 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 ** GNU General Public License for more details. 00017 ** 00018 ** You should have received a copy of the GNU General Public License 00019 ** along with this program in a file called COPYING; if not, write to 00020 ** the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 00021 ** MA 02111-1307, USA. 00022 */ 00023 00024 /* 00025 ** Bug reports and questions can be sent to kde-pim@kde.org. 00026 */ 00027 00028 #include "options.h" 00029 00030 #include <pi-version.h> 00031 00032 #include <qtimer.h> 00033 #include <qdir.h> 00034 #include <qfileinfo.h> 00035 #include <qregexp.h> 00036 #include <kconfig.h> 00037 #include <kdebug.h> 00038 00039 #include <pilotSysInfo.h> 00040 #include <pilotUser.h> 00041 #include <pilotCard.h> 00042 #include <kpilotlink.h> 00043 #include <kstandarddirs.h> 00044 #include <pilotSerialDatabase.h> 00045 00046 #include <sys/utsname.h> 00047 00048 #include "sysinfo-factory.h" 00049 #include "sysinfo-conduit.moc" 00050 00051 const QString SysInfoConduit::defaultpage = QString("KPilot System Information Page\n" 00052 "==============================\n" 00053 "(Kpilot was unable to find the correct template file, \n" 00054 "so this simple template was used.)\n\n" 00055 "<!--#ifhardware#\n" 00056 "-) Hardware Information\n" 00057 " DeviceID: #deviceid#\n" 00058 " Device name: #devicename#\n" 00059 " Device model: #devicemodel#\n" 00060 " Manufacturer: #manufacturer#\n" 00061 " Connected via: #devicetype#\n" 00062 "#endifhardware#-->\n" 00063 "\n" 00064 "<!--#ifuser#\n" 00065 "-) User Information\n" 00066 " Handheld User Name: #username#\n" 00067 " Handheld Password: #pw#\n" 00068 " Handheld User ID: #uid#\n" 00069 " Viewer ID: #viewerid#\n" 00070 "#endifuser#-->\n" 00071 "\n" 00072 "<!--#ifmemory#\n" 00073 "-) Memory Information\n" 00074 " ROM: #rom# kB total\n" 00075 " Total RAM: #totalmem# kB total\n" 00076 " Free RAM: #freemem# kB free\n" 00077 "#endifmemory#-->\n" 00078 "\n" 00079 "<!--#ifstorage#\n" 00080 "-) Storage Information\n" 00081 " Number of cards: #cards#\n" 00082 " Memory on cards: #storagemem#\n" 00083 "#endifstorage#-->\n" 00084 "\n" 00085 "<!--#ifdblist#\n" 00086 "-) List of Databases on Handheld\n" 00087 " Available Databases: #dblist(%1,)#\n" 00088 "#endifdblist#-->\n" 00089 "\n" 00090 "<!--#ifrecords#\n" 00091 "-) Number of addresses, todos, events, and memos\n" 00092 " Addresses: #addresses# entries in Addressbook\n" 00093 " Events: #events# entries in Calendar\n" 00094 " Todos: #todos# entries in ToDo list\n" 00095 " Memos: #memos# memos\n" 00096 "#endifrecords#-->\n" 00097 "\n" 00098 "<!--#ifsync#\n" 00099 "-) Synchronization Information\n" 00100 " Last sync attempt: #lastsync#\n" 00101 " Last successful sync: #lastsuccsync#\n" 00102 " Last sync with PC (ID): #lastsyncpc#\n" 00103 "#endifsync#-->\n" 00104 "\n" 00105 "<!--#ifpcversion#\n" 00106 "-) Version Information (Desktop)\n" 00107 " Operating System: #os#\n" 00108 " Hostname: #hostname#\n" 00109 " Qt Version: #qt#\n" 00110 " KDE Version: #kde#\n" 00111 " KPilot Version: #kpilot#\n" 00112 " Pilot-Link Version: #pilotlink#\n" 00113 "#endifpcversion#-->\n" 00114 "\n" 00115 "<!--#ifpalmversion#\n" 00116 "-) Version Information (Handheld)\n" 00117 " PalmOS: #palmos#\n" 00118 "#endifpalmversion#-->\n" 00119 "\n" 00120 "<!--#ifdebug#\n" 00121 "-) Debug Information\n" 00122 " #debug#\n" 00123 "#endifdebug#-->\n" 00124 "\n" 00125 "------------------------------------------------------------\n" 00126 "Page created <!--#date#--> by the KPilot System Information conduit.\n" 00127 ""); 00128 00129 00144 // Something to allow us to check what revision 00145 // the modules are that make up a binary distribution. 00146 const char *SysInfo_conduit_id = 00147 "$Id: sysinfo-conduit.cc,v 1.8 2003/08/12 18:11:51 mueller Exp $"; 00148 00149 00150 00151 00152 SysInfoConduit::SysInfoConduit(KPilotDeviceLink * o, 00153 const char *n, 00154 const QStringList & a) : 00155 ConduitAction(o, n, a) 00156 { 00157 FUNCTIONSETUP; 00158 #ifdef DEBUG 00159 DEBUGCONDUIT<<SysInfo_conduit_id<<endl; 00160 #endif 00161 fConduitName=i18n("System Information"); 00162 } 00163 00164 00165 00166 SysInfoConduit::~SysInfoConduit() 00167 { 00168 FUNCTIONSETUP; 00169 } 00170 00171 00172 00173 void SysInfoConduit::readConfig() 00174 { 00175 FUNCTIONSETUP; 00176 KConfigGroupSaver g(fConfig, SysInfoConduitFactory::fGroup); 00177 fOutputFile=fConfig->readPathEntry(SysInfoConduitFactory::fOutputFile); 00178 fTemplateFile=fConfig->readPathEntry(SysInfoConduitFactory::fTemplateFile); 00179 fOutputType=(eOutputTypeEnum)(fConfig->readNumEntry(SysInfoConduitFactory::fOutputType, 0)); 00180 fHardwareInfo=fConfig->readBoolEntry(SysInfoConduitFactory::fHardwareInfo, true); 00181 fUserInfo=fConfig->readBoolEntry(SysInfoConduitFactory::fUserInfo, true); 00182 fMemoryInfo=fConfig->readBoolEntry(SysInfoConduitFactory::fMemoryInfo, true); 00183 fStorageInfo=fConfig->readBoolEntry(SysInfoConduitFactory::fStorageInfo, true); 00184 fDBList=fConfig->readBoolEntry(SysInfoConduitFactory::fDBList, true); 00185 fRecordNumber=fConfig->readBoolEntry(SysInfoConduitFactory::fRecordNumber, true); 00186 fSyncInfo=fConfig->readBoolEntry(SysInfoConduitFactory::fSyncInfo, true); 00187 fKDEVersion=fConfig->readBoolEntry(SysInfoConduitFactory::fKDEVersion, true); 00188 fPalmOSVersion=fConfig->readBoolEntry(SysInfoConduitFactory::fPalmOSVersion, true); 00189 fDebugInfo=fConfig->readBoolEntry(SysInfoConduitFactory::fDebugInfo, true); 00190 #ifdef DEBUG 00191 DEBUGCONDUIT<<"Output file="<<fOutputFile<<" with type "<< 00192 fOutputType<<" (Template:"<<fTemplateFile<<")"<<endl; 00193 DEBUGCONDUIT<<"HW:"<<fHardwareInfo<<",User:"<<fUserInfo<< 00194 ",Mem:"<<fMemoryInfo<<",Sto:"<<fStorageInfo<<endl; 00195 DEBUGCONDUIT<<"DBL:"<<fDBList<<",Rec:"<<fRecordNumber<< 00196 ",KDE:"<<fKDEVersion<<",PalmOS:"<<fPalmOSVersion<<endl; 00197 #endif 00198 } 00199 00200 00201 /* virtual */ bool SysInfoConduit::exec() 00202 { 00203 FUNCTIONSETUP; 00204 DEBUGCONDUIT<<SysInfo_conduit_id<<endl; 00205 00206 if (!fConfig) 00207 { 00208 kdWarning() << k_funcinfo << ": No config file was set!" << endl; 00209 return false; 00210 } 00211 00212 readConfig(); 00213 00214 QTimer::singleShot(0, this, SLOT(hardwareInfo())); 00215 return true; 00216 } 00217 00218 void SysInfoConduit::hardwareInfo() 00219 { 00220 FUNCTIONSETUP; 00221 if (fHardwareInfo) { 00222 /* Retrieve values for 00223 * - #deviceid# 00224 * - #devicename# 00225 * - #devicemodel# 00226 * - #manufactorer# 00227 * - #devicetype# 00228 */ 00229 fValues["deviceid"] = QString(fHandle->getSysInfo()->getProductID()); 00230 KPilotCard*device = fHandle->getCardInfo(); 00231 fValues["devicename"] = QString(device->getCardName()); 00232 fValues["devicemodel"] = i18n("unknown"); // TODO 00233 fValues["manufacturer"] = QString(device->getCardManufacturer()); 00234 fValues["devicetype"] = QString( 00235 fHandle->deviceTypeString(fHandle->deviceType())); 00236 KPILOT_DELETE(device); 00237 keepParts.append("hardware"); 00238 } else removeParts.append("hardware"); 00239 QTimer::singleShot(0, this, SLOT(userInfo())); 00240 } 00241 00242 void SysInfoConduit::userInfo() 00243 { 00244 FUNCTIONSETUP; 00245 if (fUserInfo) { 00246 /* Retrieve values for 00247 * - #username# 00248 * - #uid# 00249 */ 00250 KPilotUser*user=fHandle->getPilotUser(); 00251 fValues["username"] = user->getUserName(); 00252 if (user->getPasswordLength()>0) 00253 fValues["pw"] = i18n("Password set"); 00254 else 00255 fValues["pw"] = i18n("No password set"); 00256 fValues["uid"] = QString::number(user->getUserID()); 00257 fValues["viewerid"] = QString::number(user->getViewerID()); 00258 keepParts.append("user"); 00259 } else removeParts.append("user"); 00260 QTimer::singleShot(0, this, SLOT(memoryInfo())); 00261 } 00262 00263 void SysInfoConduit::memoryInfo() 00264 { 00265 FUNCTIONSETUP; 00266 if (fMemoryInfo) { 00267 /* Retrieve values for 00268 * - #rom# 00269 * - #totalmem# 00270 * - #freemem# 00271 */ 00272 KPilotCard*device = fHandle->getCardInfo(); 00273 fValues["rom"] = QString::number(device->getRomSize()/1024); 00274 fValues["totalmem"] = QString::number(device->getRamSize()/1024); 00275 fValues["freemem"] = QString::number(device->getRamFree()/1024); 00276 keepParts.append("memory"); 00277 } else removeParts.append("memory"); 00278 QTimer::singleShot(0, this, SLOT(storageInfo())); 00279 } 00280 00281 void SysInfoConduit::storageInfo() 00282 { 00283 FUNCTIONSETUP; 00284 if (fStorageInfo) { 00285 /* Retrieve values for 00286 * - $cards$ 00287 */ 00288 KPilotCard*device = fHandle->getCardInfo(1); 00289 if (device && device) { 00290 fValues["cards"] = QString("%1 (%2, %3 kB of %3 kB free)") 00291 .arg(device->getCardName()) 00292 .arg(device->getCardManufacturer()) 00293 .arg(device->getRamFree()/1024) 00294 .arg(device->getRamSize()/1024); 00295 KPILOT_DELETE(device); 00296 } else { 00297 fValues["cards"] = i18n("No Cards available via pilot-link"); 00298 } 00299 keepParts.append("storage"); 00300 } else removeParts.append("storage"); 00301 QTimer::singleShot(0, this, SLOT(dbListInfo())); 00302 } 00303 00304 void SysInfoConduit::dbListInfo() 00305 { 00306 FUNCTIONSETUP; 00307 if (fDBList) { 00308 /* Retrieve values for 00309 * - #dblist(structure)# 00310 */ 00311 dblist=fHandle->getDBList(); 00312 keepParts.append("dblist"); 00313 } else removeParts.append("dblist"); 00314 QTimer::singleShot(0, this, SLOT(recNumberInfo())); 00315 } 00316 00317 void SysInfoConduit::recNumberInfo() 00318 { 00319 FUNCTIONSETUP; 00320 if (fRecordNumber) { 00321 /* Retrieve values for 00322 * - #addresses# 00323 * - #events# 00324 * - #todos# 00325 * - #memos# 00326 */ 00327 PilotDatabase*fDatabase; 00328 fValues["addresses"] = "ERROR"; 00329 fValues["events"] = "ERROR"; 00330 fValues["todos"] = "ERROR"; 00331 fValues["memos"] = "ERROR"; 00332 fDatabase = new PilotSerialDatabase(pilotSocket(), "AddressDB", this); 00333 if (fDatabase) { 00334 fValues["addresses"] = QString::number(fDatabase->recordCount()); 00335 KPILOT_DELETE(fDatabase); 00336 } 00337 fDatabase = new PilotSerialDatabase(pilotSocket(), "DatebookDB", this); 00338 if (fDatabase) { 00339 fValues["events"] = QString::number(fDatabase->recordCount()); 00340 KPILOT_DELETE(fDatabase); 00341 } 00342 fDatabase = new PilotSerialDatabase(pilotSocket(), "ToDoDB", this); 00343 if (fDatabase) { 00344 fValues["todos"] = QString::number(fDatabase->recordCount()); 00345 KPILOT_DELETE(fDatabase); 00346 } 00347 fDatabase = new PilotSerialDatabase(pilotSocket(), "MemoDB", this); 00348 if (fDatabase) { 00349 fValues["memos"] = QString::number(fDatabase->recordCount()); 00350 KPILOT_DELETE(fDatabase); 00351 } 00352 keepParts.append("records"); 00353 } else removeParts.append("records"); 00354 QTimer::singleShot(0, this, SLOT(syncInfo())); 00355 } 00356 00357 void SysInfoConduit::syncInfo() 00358 { 00359 FUNCTIONSETUP; 00360 if (fSyncInfo) { 00361 /* Retrieve values for 00362 * - #lastsync# 00363 * - #lastsuccsync# 00364 * - #lastsyncpc# 00365 */ 00366 KPilotUser*user=fHandle->getPilotUser(); 00367 time_t lastsync = user->getLastSyncDate(); 00368 QDateTime qlastsync; 00369 qlastsync.setTime_t(lastsync); 00370 fValues["lastsync"] = qlastsync.toString(Qt::LocalDate); 00371 lastsync = user->getLastSuccessfulSyncDate(); 00372 qlastsync.setTime_t(lastsync); 00373 fValues["lastsuccsync"] = qlastsync.toString(Qt::LocalDate); 00374 fValues["lastsyncpc"] = QString::number(user->getLastSyncPC()); 00375 keepParts.append("sync"); 00376 } else removeParts.append("sync"); 00377 QTimer::singleShot(0, this, SLOT(pcVersionInfo())); 00378 } 00379 00380 void SysInfoConduit::pcVersionInfo() 00381 { 00382 FUNCTIONSETUP; 00383 if (fKDEVersion) { 00384 /* Retrieve values for 00385 * - #os# 00386 * - #qt# 00387 * - #kde# 00388 * - #kpilot# 00389 * - #pilotlink# 00390 */ 00391 fValues["kpilot"] = QString::fromLatin1(KPILOT_VERSION); 00392 fValues["kde"] = i18n("unknown"); 00393 fValues["qt"] = i18n("unknown"); 00394 fValues["os"] = i18n("unknown"); 00395 fValues["hostname"] = i18n("unknown"); 00396 struct utsname name; 00397 if (uname (&name) >= 0) { 00398 fValues["os"] = QString("%1 %3, %5") 00399 .arg(name.sysname) 00400 .arg(name.release) 00401 .arg(name.machine); 00402 fValues["hostname"] = QString("%2").arg(name.nodename); 00403 } 00404 #ifdef KDE_VERSION_STRING 00405 fValues["kde"] = QString::fromLatin1(KDE_VERSION_STRING); 00406 #endif 00407 #ifdef QT_VERSION_STR 00408 fValues["qt"] = QString::fromLatin1(QT_VERSION_STR); 00409 #endif 00410 fValues["pilotlink"] = CSL1("%1.%2.%3%4") 00411 .arg(PILOT_LINK_VERSION) 00412 .arg(PILOT_LINK_MAJOR) 00413 .arg(PILOT_LINK_MINOR) 00414 #ifdef PILOT_LINK_PATCH 00415 .arg(QString::fromLatin1(PILOT_LINK_PATCH)); 00416 #else 00417 .arg(QString()); 00418 #endif 00419 keepParts.append("pcversion"); 00420 } else removeParts.append("pcversion"); 00421 QTimer::singleShot(0, this, SLOT(palmVersionInfo())); 00422 } 00423 00424 void SysInfoConduit::palmVersionInfo() 00425 { 00426 FUNCTIONSETUP; 00427 if (fPalmOSVersion) { 00428 /* Retrieve values for 00429 * - #palmos# 00430 */ 00431 /* fValues["palmos"] = QString("PalmOSŪ %1.%2 (compat %3.%4)") 00432 .arg(fHandle->getSysInfo()->getMajorVersion()) 00433 .arg(fHandle->getSysInfo()->getMinorVersion()) 00434 .arg(fHandle->getSysInfo()->getCompatMajorVersion()) 00435 .arg(fHandle->getSysInfo()->getCompatMinorVersion());*/ 00436 fValues["palmos"] = QString("PalmOSŪ %1.%2").arg(fHandle->majorVersion()).arg(fHandle->minorVersion()); 00437 00438 keepParts.append("palmversion"); 00439 } else removeParts.append("palmversion"); 00440 QTimer::singleShot(0, this, SLOT(debugInfo())); 00441 } 00442 00443 void SysInfoConduit::debugInfo() 00444 { 00445 FUNCTIONSETUP; 00446 if (fDebugInfo) { 00447 /* Retrieve values for 00448 * - #debug# 00449 */ 00450 fValues["debug"] = i18n("No debug data"); 00451 keepParts.append("debug"); 00452 } else removeParts.append("debug"); 00453 QTimer::singleShot(0, this, SLOT(writeFile())); 00454 } 00455 00456 void SysInfoConduit::writeFile() 00457 { 00458 FUNCTIONSETUP; 00459 00460 fValues["date"] = QDateTime::currentDateTime().toString(Qt::LocalDate); 00461 00462 QString output; 00463 // Open the template file 00464 QString templatefile; 00465 switch(fOutputType) 00466 { 00467 case eOutputText: 00468 templatefile=locate("data", "kpilot/sysinfoconduit/Template.txt"); 00469 break; 00470 case eOutputTemplate: 00471 templatefile=fTemplateFile; 00472 break; 00473 case eOutputHTML: 00474 default: 00475 templatefile=locate("data", "kpilot/sysinfoconduit/Template.html"); 00476 break; 00477 } 00478 00479 // Read in the template, close the file 00480 bool loaded=false; 00481 if (!templatefile.isEmpty()){ 00482 #ifdef DEBUG 00483 DEBUGCONDUIT<<"Loading template file "<<templatefile<<endl; 00484 #endif 00485 QFile infile(templatefile); 00486 if (infile.open(IO_ReadOnly)) { 00487 QTextStream instream(&infile); 00488 output = instream.read(); 00489 infile.close(); 00490 loaded=true; 00491 } 00492 } 00493 00494 if (!loaded) { 00495 kdWarning()<<"Loading template file "<<templatefile<<" failed. Using default template instead."<< endl; 00496 output=defaultpage; 00497 } 00498 00499 // Remove all parts not extracted 00500 for ( QStringList::Iterator it = removeParts.begin(); it != removeParts.end(); ++it ) { 00501 QRegExp re(QString("<!--#if%1#.*#endif%1#-->").arg(*it).arg(*it)); 00502 re.setMinimal(true); 00503 output.remove(re); 00504 } 00505 for ( QStringList::Iterator it = keepParts.begin(); it != keepParts.end(); ++it ) { 00506 QRegExp re(QString("<!--#if%1#(.*)#endif%1#-->").arg(*it).arg(*it)); 00507 re.setMinimal(true); 00508 output.replace(re, "\\1"); 00509 } 00510 00511 // Do a loop through all keys in fValues 00512 QMap<QString,QString>::Iterator it; 00513 for ( it = fValues.begin(); it != fValues.end(); ++it ) { 00514 output.replace(QString("#%1#").arg(it.key().latin1()), it.data().latin1()); 00515 } 00516 00517 // Insert the list of databases 00518 QRegExp re("#dblist\\[(.*)\\]#"); 00519 re.setMinimal(true); 00520 while (re.search(output)>=0){ 00521 QString dbstring; 00522 QString subpatt=re.cap(1); 00523 DBInfo*dbi; 00524 for (dbi=dblist.first(); dbi; dbi=dblist.next() ) { 00525 QString newpatt(subpatt); 00526 char tmpchr[5]; 00527 ::memset(&tmpchr[0], 0, 5); 00528 /* Patterns for the dblist argument: 00529 * %0 .. Database name 00530 * %1 .. type 00531 * %2 .. creator 00532 * %3 .. index 00533 * %4 .. flags 00534 * %5 .. miscFlags 00535 * %6 .. version 00536 * %7 .. createDate 00537 * %8 .. modifyDate 00538 * %9 .. backupDate 00539 */ 00540 newpatt.replace("%0", QString(dbi->name)); 00541 set_long(&tmpchr[0],dbi->type); 00542 newpatt.replace("%1", QString(tmpchr)); 00543 set_long(&tmpchr[0],dbi->creator); 00544 newpatt.replace("%2", tmpchr); 00545 newpatt.replace("%3", QString::number(dbi->index)); 00546 newpatt.replace("%4", QString::number(dbi->flags)); 00547 newpatt.replace("%5", QString::number(dbi->miscFlags)); 00548 newpatt.replace("%6", QString::number(dbi->version)); 00549 QDateTime tm; 00550 tm.setTime_t(dbi->createDate); 00551 newpatt.replace("%7", tm.toString(Qt::LocalDate)); 00552 tm.setTime_t(dbi->modifyDate); 00553 newpatt.replace("%8", tm.toString(Qt::LocalDate)); 00554 tm.setTime_t(dbi->backupDate); 00555 newpatt.replace("%9", tm.toString(Qt::LocalDate)); 00556 00557 dbstring.append(newpatt); 00558 } 00559 // Now, just replace the whole found pattern by the string we just constructed. 00560 output.replace(re.cap(0), dbstring); 00561 } 00562 00563 // Write out the result 00564 QFile outfile(fOutputFile); 00565 if (fOutputFile.isEmpty() || (!outfile.open(IO_WriteOnly)) ) { 00566 QFileInfo fi(QDir::home(), QString("KPilotSysInfo.")+QFileInfo(templatefile).extension() ); 00567 fOutputFile=fi.absFilePath(); 00568 kdWarning()<<i18n("Unable to open output file, using %1 instead.").arg(fOutputFile).latin1()<<endl; 00569 emit logMessage(i18n("Unable to open output file, using %1 instead.").arg(fOutputFile)); 00570 outfile.setName(fOutputFile); 00571 if (!outfile.open(IO_WriteOnly)) { 00572 kdWarning()<<i18n("Unable to open %1").arg(fOutputFile).latin1()<<endl; 00573 emit logError(i18n("Unable to open %1").arg(fOutputFile)); 00574 QTimer::singleShot(0, this, SLOT(cleanup())); 00575 return; 00576 } 00577 } 00578 00579 // Finally, write the actual text out to the file. 00580 QTextStream outstream(&outfile); 00581 outstream<<output; 00582 outfile.close(); 00583 00584 emit logMessage(i18n("Handheld system information written to the file %1").arg(fOutputFile)); 00585 QTimer::singleShot(0, this, SLOT(cleanup())); 00586 } 00587 00588 void SysInfoConduit::cleanup() 00589 { 00590 FUNCTIONSETUP; 00591 // Nothing to clean up so far (Do I have memory leaks somewhere???) 00592 emit syncDone(this); 00593 }
KDE Logo
This file is part of the documentation for kpilot Library Version 3.2.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Jul 28 23:57:49 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003