00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <qdir.h>
00022 #include <qmessagebox.h>
00023 #if QT_VERSION <= 230 && defined(QT_NO_CODECS)
00024 #include <qtextcodec.h>
00025 #endif
00026 #include <qtextstream.h>
00027
00028 #include <sys/stat.h>
00029 #include <sys/types.h>
00030 #include <sys/time.h>
00031 #include <fcntl.h>
00032 #include <stdlib.h>
00033 #include <time.h>
00034 #include <unistd.h>
00035
00036 #define QTOPIA_INTERNAL_LANGLIST
00037 #include "config.h"
00038 #include "global.h"
00039 #include <qtopia/qpeapplication.h>
00040
00041
00042
00043
00044
00045 class ConfigPrivate {
00046 public:
00047 ConfigPrivate() : multilang(FALSE) {}
00048 ConfigPrivate(const ConfigPrivate& o) :
00049 trfile(o.trfile),
00050 trcontext(o.trcontext),
00051 multilang(o.multilang)
00052 {}
00053 ConfigPrivate& operator=(const ConfigPrivate& o)
00054 {
00055 trfile = o.trfile;
00056 trcontext = o.trcontext;
00057 multilang = o.multilang;
00058 return *this;
00059 }
00060
00061 QString trfile;
00062 QCString trcontext;
00063 bool multilang;
00064 };
00065
00068
00069 #ifndef Q_OS_WIN32
00070
00071
00072 class ConfigData
00073 {
00074 public:
00075 ConfigData() {}
00076 ConfigData( const ConfigGroupMap& cf, const ConfigPrivate& pri,
00077 struct stat sbuf )
00078 : cfg( cf ), priv( pri ), mtime( sbuf.st_mtime ),
00079 size( sbuf.st_size )
00080 {
00081 gettimeofday(&used, 0 );
00082 }
00083
00084 ConfigGroupMap cfg;
00085 ConfigPrivate priv;
00086 time_t mtime;
00087 unsigned int size;
00088 struct timeval used;
00089 };
00090
00091
00092 class ConfigCache : public QObject {
00093 public:
00094 static ConfigCache* instance();
00095
00096 void insert( const QString& fileName, const ConfigGroupMap& cfg,
00097 const ConfigPrivate *priv );
00098 bool find(const QString& fileName, ConfigGroupMap& cfg,
00099 ConfigPrivate** priv );
00100 protected:
00101 void timerEvent( QTimerEvent* );
00102
00103 private:
00104 ConfigCache();
00105 void remove( const QString& fileName );
00106 void removeLru();
00107
00108 private:
00109 QMap<QString, ConfigData> m_cached;
00110 unsigned int m_totalSize;
00111 int m_tid;
00112 private:
00113 static ConfigCache* m_inst;
00114 static const unsigned int CONFIG_CACHE_SIZE = 8192;
00115 static const unsigned int CONFIG_CACHE_TIMEOUT = 1000;
00116 };
00117
00118 ConfigCache* ConfigCache::m_inst = 0;
00119
00120
00121
00122 ConfigCache::ConfigCache() : QObject( qApp ), m_totalSize( 0 ), m_tid( 0 ) {}
00123 ConfigCache* ConfigCache::instance() {
00124 if ( !m_inst )
00125 m_inst = new ConfigCache();
00126
00127 return m_inst;
00128 }
00129
00130 void ConfigCache::remove( const QString& fileName ) {
00131 QMap<QString, ConfigData>::Iterator it = m_cached.find( fileName );
00132
00133 if ( it == m_cached.end() )
00134 return;
00135
00136 m_totalSize -= (*it).size;
00137 m_cached.remove( it );
00138 }
00139
00140 void ConfigCache::removeLru() {
00141 QMap<QString, ConfigData>::Iterator it = m_cached.begin();
00142 QMap<QString, ConfigData>::Iterator lru = it;
00143 ++it;
00144 for (; it != m_cached.end(); ++it)
00145 if ((*it).used.tv_sec < (*lru).used.tv_sec ||
00146 ((*it).used.tv_sec == (*lru).used.tv_sec &&
00147 (*it).used.tv_usec < (*lru).used.tv_usec))
00148 lru = it;
00149
00150 m_totalSize -= (*lru).size;
00151 m_cached.remove(lru);
00152 }
00153
00154 void ConfigCache::timerEvent( QTimerEvent* ) {
00155 while ( m_totalSize > CONFIG_CACHE_SIZE )
00156 removeLru();
00157
00158 killTimer(m_tid);
00159 m_tid = 0;
00160 }
00161
00162 void ConfigCache::insert( const QString& fileName, const ConfigGroupMap& cfg,
00163 const ConfigPrivate* _priv ) {
00164
00165
00166 struct stat sbuf;
00167 ::stat( QFile::encodeName(fileName), &sbuf );
00168 if ( static_cast<unsigned int>(sbuf.st_size) >= CONFIG_CACHE_SIZE>>1)
00169 return;
00170
00171
00172
00173
00174 ConfigPrivate priv = _priv ? *_priv : ConfigPrivate();
00175 ConfigData data( cfg, priv, sbuf );
00176 m_totalSize += data.size;
00177
00178 remove( fileName );
00179 m_cached.insert( fileName, data );
00180
00181
00182
00183
00184
00185 if ( m_totalSize >= CONFIG_CACHE_SIZE )
00186 if ( !m_tid )
00187 m_tid = startTimer(CONFIG_CACHE_TIMEOUT);
00188 }
00189
00190 bool ConfigCache::find( const QString& fileName, ConfigGroupMap& cfg,
00191 ConfigPrivate **ppriv ) {
00192 QMap<QString, ConfigData>::Iterator it = m_cached.find(fileName);
00193 if (it != m_cached.end()) {
00194 ConfigData &data = *it;
00195 struct stat sbuf;
00196 ::stat(QFile::encodeName( fileName ), &sbuf);
00197
00198 if (data.mtime == sbuf.st_mtime && (int)data.size == sbuf.st_size) {
00199 cfg = data.cfg;
00200
00201
00202
00203
00204 if ( *ppriv == 0 )
00205 *ppriv = new ConfigPrivate( data.priv );
00206 **ppriv = data.priv;
00207 gettimeofday(&data.used, 0);
00208
00209 return true;
00210 }
00211 }
00212
00213 return false;
00214 }
00215
00216 #endif
00217
00218
00222 QString Config::configFilename(const QString& name, Domain d)
00223 {
00224 switch (d) {
00225 case File:
00226 return name;
00227 case User: {
00228 QDir dir = (QString(getenv("HOME")) + "/Settings");
00229 if ( !dir.exists() )
00230 mkdir(dir.path().local8Bit(),0700);
00231 return dir.path() + "/" + name + ".conf";
00232 }
00233 }
00234 return name;
00235 }
00236
00237
00238 void Config::read( QTextStream &s )
00239 {
00240 #if QT_VERSION <= 230 && defined(QT_NO_CODECS)
00241
00242 s.setCodec( QTextCodec::codecForMib( 106 ) );
00243 #else
00244 s.setEncoding( QTextStream::UnicodeUTF8 );
00245 #endif
00246
00247 QStringList list = QStringList::split('\n', s.read() );
00248
00249 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
00250 if ( !parse( *it ) ) {
00251 git = groups.end();
00252 return;
00253 }
00254 }
00255 }
00256
00293 Config::Config( const QString &name, Domain domain )
00294 : filename( configFilename(name,domain) )
00295 {
00296 git = groups.end();
00297 d = 0;
00298 read();
00299 }
00300
00301
00302
00303 Config::Config ( const QString &name, bool what )
00304 : filename( configFilename(name,what ? User : File) )
00305 {
00306 git = groups.end();
00307 d = 0;
00308 read();
00309 }
00310
00314 Config::~Config()
00315 {
00316 if ( changed )
00317 write();
00318
00319 delete d;
00320 }
00321
00325 bool Config::hasKey( const QString &key ) const
00326 {
00327 if ( groups.end() == git )
00328 return FALSE;
00329 ConfigGroup::ConstIterator it = ( *git ).find( key );
00330 if ( it == ( *git ).end() ) {
00331 if ( d && !d->trcontext.isNull() ) {
00332 it = ( *git ).find( key + "[]" );
00333 } else if ( d && d->multilang ) {
00334 it = ( *git ).find( key + "["+lang+"]" );
00335 if ( it == ( *git ).end() && !glang.isEmpty() )
00336 it = ( *git ).find( key + "["+glang+"]" );
00337 }
00338 }
00339 return it != ( *git ).end();
00340 }
00341
00351 void Config::setGroup( const QString &gname )
00352 {
00353 QMap< QString, ConfigGroup>::Iterator it = groups.find( gname );
00354 if ( it == groups.end() ) {
00355 git = groups.insert( gname, ConfigGroup() );
00356 changed = TRUE;
00357 return;
00358 }
00359 git = it;
00360 }
00361
00367 void Config::writeEntry( const QString &key, const char* value )
00368 {
00369 writeEntry(key,QString(value));
00370 }
00371
00377 void Config::writeEntry( const QString &key, const QString &value )
00378 {
00379 if ( git == groups.end() ) {
00380 qWarning( "no group set" );
00381 return;
00382 }
00383 if ( (*git)[key] != value ) {
00384 ( *git ).insert( key, value );
00385 changed = TRUE;
00386 }
00387 }
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398 static QString encipher(const QString& plain)
00399 {
00400
00401 QString cipher;
00402 int mix=28730492;
00403 for (int i=0; i<(int)plain.length(); i++) {
00404 int u = plain[i].unicode();
00405 int c = u ^ mix;
00406 QString x = QString::number(c,36);
00407 cipher.append(QChar('a'+x.length()));
00408 cipher.append(x);
00409 mix *= u;
00410 }
00411 return cipher;
00412 }
00413
00414 static QString decipher(const QString& cipher)
00415 {
00416 QString plain;
00417 int mix=28730492;
00418 for (int i=0; i<(int)cipher.length();) {
00419 int l = cipher[i].unicode()-'a';
00420 QString x = cipher.mid(i+1,l); i+=l+1;
00421 int u = x.toInt(0,36) ^ mix;
00422 plain.append(QChar(u));
00423 mix *= u;
00424 }
00425 return plain;
00426 }
00427
00437 void Config::writeEntryCrypt( const QString &key, const QString &value )
00438 {
00439 if ( git == groups.end() ) {
00440 qWarning( "no group set" );
00441 return;
00442 }
00443 QString evalue = encipher(value);
00444 if ( (*git)[key] != evalue ) {
00445 ( *git ).insert( key, evalue );
00446 changed = TRUE;
00447 }
00448 }
00449
00455 void Config::writeEntry( const QString &key, int num )
00456 {
00457 QString s;
00458 s.setNum( num );
00459 writeEntry( key, s );
00460 }
00461
00462 #ifdef Q_HAS_BOOL_TYPE
00463
00469 void Config::writeEntry( const QString &key, bool b )
00470 {
00471 QString s;
00472 s.setNum( ( int )b );
00473 writeEntry( key, s );
00474 }
00475 #endif
00476
00483 void Config::writeEntry( const QString &key, const QStringList &lst, const QChar &sep )
00484 {
00485 QString s;
00486 QStringList::ConstIterator it = lst.begin();
00487 for ( ; it != lst.end(); ++it )
00488 s += *it + sep;
00489 writeEntry( key, s );
00490 }
00491
00497 void Config::removeEntry( const QString &key )
00498 {
00499 if ( git == groups.end() ) {
00500 qWarning( "no group set" );
00501 return;
00502 }
00503 ( *git ).remove( key );
00504 changed = TRUE;
00505 }
00506
00526
00527
00528
00529
00530
00535 QString Config::readEntry( const QString &key, const QString &deflt )
00536 {
00537 QString r;
00538 if ( d && !d->trcontext.isNull() ) {
00539
00540
00541
00542 r = readEntryDirect( key );
00543 if ( !r.isNull() )
00544 return r;
00545 r = readEntryDirect( key + "[]" );
00546 if ( !r.isNull() )
00547 return qApp->translate(d->trfile,d->trcontext,r);
00548 } else if ( d && d->multilang ) {
00549
00550 r = readEntryDirect( key + "["+lang+"]" );
00551 if ( !r.isNull() )
00552 return r;
00553 if ( !glang.isEmpty() ) {
00554 r = readEntryDirect( key + "["+glang+"]" );
00555 if ( !r.isNull() )
00556 return r;
00557 }
00558 }
00559 r = readEntryDirect( key, deflt );
00560 return r;
00561 }
00562
00573 QString Config::readEntryCrypt( const QString &key, const QString &deflt )
00574 {
00575 QString res = readEntry( key );
00576 if ( res.isNull() )
00577 return deflt;
00578 return decipher(res);
00579 }
00580
00590 QString Config::readEntryDirect( const QString &key, const QString &deflt )
00591 {
00592 if ( git == groups.end() ) {
00593
00594 return deflt;
00595 }
00596 ConfigGroup::ConstIterator it = ( *git ).find( key );
00597 if ( it != ( *git ).end() )
00598 return *it;
00599 else
00600 return deflt;
00601 }
00602
00612 int Config::readNumEntry( const QString &key, int deflt )
00613 {
00614 QString s = readEntry( key );
00615 if ( s.isEmpty() )
00616 return deflt;
00617 else
00618 return s.toInt();
00619 }
00620
00630 bool Config::readBoolEntry( const QString &key, bool deflt )
00631 {
00632 QString s = readEntry( key );
00633 if ( s.isEmpty() )
00634 return deflt;
00635 else
00636 return (bool)s.toInt();
00637 }
00638
00648 QStringList Config::readListEntry( const QString &key, const QChar &sep )
00649 {
00650 QString s = readEntry( key );
00651 if ( s.isEmpty() )
00652 return QStringList();
00653 else
00654 return QStringList::split( sep, s );
00655 }
00656
00660 void Config::clearGroup()
00661 {
00662 if ( git == groups.end() ) {
00663 qWarning( "no group set" );
00664 return;
00665 }
00666 if ( !(*git).isEmpty() ) {
00667 ( *git ).clear();
00668 changed = TRUE;
00669 }
00670 }
00671
00675 void Config::write( const QString &fn )
00676 {
00677 QString oldGroup = git.key();
00678
00679 QString strNewFile;
00680 if ( !fn.isEmpty() )
00681 filename = fn;
00682 strNewFile = filename + ".new";
00683
00684 QFile f( strNewFile );
00685 if ( !f.open( IO_WriteOnly|IO_Raw ) ) {
00686 qWarning( "could not open for writing `%s'", strNewFile.latin1() );
00687 git = groups.end();
00688 return;
00689 }
00690
00691 QString str;
00692 QCString cstr;
00693 QMap< QString, ConfigGroup >::Iterator g_it = groups.begin();
00694
00695 for ( ; g_it != groups.end(); ++g_it ) {
00696 str += "[" + g_it.key() + "]\n";
00697 ConfigGroup::Iterator e_it = ( *g_it ).begin();
00698 for ( ; e_it != ( *g_it ).end(); ++e_it )
00699 str += e_it.key() + " = " + *e_it + "\n";
00700 }
00701 cstr = str.utf8();
00702
00703 int total_length;
00704 total_length = f.writeBlock( cstr.data(), cstr.length() );
00705 if ( total_length != int(cstr.length()) ) {
00706 QMessageBox::critical( 0, QObject::tr("Out of Space"),
00707 QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") );
00708 f.close();
00709 QFile::remove( strNewFile );
00710 return;
00711 }
00712
00713 f.close();
00714
00715 if ( rename( strNewFile, filename ) < 0 ) {
00716 qWarning( "problem renaming the file %s to %s", strNewFile.latin1(),
00717 filename.latin1() );
00718 QFile::remove( strNewFile );
00719 return;
00720 }
00721
00722 #ifndef Q_OS_WIN32
00723 ConfigCache::instance()->insert( filename, groups, d );
00724 setGroup( oldGroup );
00725 #endif
00726 changed = FALSE;
00727 }
00728
00732 bool Config::isValid() const
00733 {
00734 return groups.end() != git;
00735 }
00736
00740 void Config::read()
00741 {
00742 changed = FALSE;
00743
00744 QString readFilename(filename);
00745
00746 if ( !QFile::exists(filename) ) {
00747 bool failed = TRUE;
00748 QFileInfo fi(filename);
00749 QString settingsDir = QDir::homeDirPath() + "/Settings";
00750 if (fi.dirPath(TRUE) == settingsDir) {
00751
00752 QString dftlFile = QPEApplication::qpeDir() + "etc/default/" + fi.fileName();
00753 if (QFile::exists(dftlFile)) {
00754 readFilename = dftlFile;
00755 failed = FALSE;
00756 }
00757 }
00758 if (failed) {
00759 git = groups.end();
00760 return;
00761 }
00762 }
00763
00764 #ifndef Q_OS_WIN32
00765
00766 if (ConfigCache::instance()->find(readFilename, groups, &d)) {
00767 if ( d && d->multilang ) {
00768 QStringList l = Global::languageList();
00769 lang = l[0];
00770 glang = l[1];
00771 }
00772 git = groups.begin();
00773 return;
00774 }
00775 #endif
00776
00777 QFile f( readFilename );
00778 if ( !f.open( IO_ReadOnly ) ) {
00779 git = groups.end();
00780 return;
00781 }
00782
00783 if (f.getch()!='[') {
00784 git = groups.end();
00785 return;
00786 }
00787 f.ungetch('[');
00788
00789 QTextStream s( &f );
00790 read( s );
00791 f.close();
00792
00793 #ifndef Q_OS_WIN32
00794 ConfigCache::instance()->insert(readFilename, groups, d);
00795 #endif
00796 }
00797
00801 bool Config::parse( const QString &l )
00802 {
00803 QString line = l.stripWhiteSpace();
00804 if ( line[ 0 ] == QChar( '[' ) ) {
00805 QString gname = line;
00806 gname = gname.remove( 0, 1 );
00807 if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) )
00808 gname = gname.remove( gname.length() - 1, 1 );
00809 git = groups.insert( gname, ConfigGroup() );
00810 } else if ( !line.isEmpty() ) {
00811 if ( git == groups.end() )
00812 return FALSE;
00813 int eq = line.find( '=' );
00814 if ( eq == -1 )
00815 return FALSE;
00816 QString key = line.left(eq).stripWhiteSpace();
00817 QString value = line.mid(eq+1).stripWhiteSpace();
00818
00819 if ( git.key() == "Translation" ) {
00820 if ( key == "File" ) {
00821 if ( !d )
00822 d = new ConfigPrivate;
00823 d->trfile = value;
00824 } else if ( key == "Context" ) {
00825 if ( !d )
00826 d = new ConfigPrivate;
00827 d->trcontext = value.latin1();
00828 } else if ( key.startsWith("Comment") ) {
00829 return TRUE;
00830 } else {
00831 return FALSE;
00832 }
00833 }
00834
00835 int kl = key.length();
00836 if ( kl > 1 && key[kl-1] == ']' && key[kl-2] != '[' ) {
00837
00838 if ( !d )
00839 d = new ConfigPrivate;
00840 if ( !d->multilang ) {
00841 QStringList l = Global::languageList();
00842 lang = l[0];
00843 glang = l[1];
00844 d->multilang = TRUE;
00845 }
00846 }
00847
00848 ( *git ).insert( key, value );
00849 }
00850 return TRUE;
00851 }
00852
00853
00854
00855 bool Config::hasGroup( const QString& name )const {
00856 return ( groups. find ( name ) != groups. end ( ));
00857 };
00858
00859 QStringList Config::groupList()const {
00860 QStringList sl;
00861 for ( ConfigGroupMap::ConstIterator it = groups. begin ( ); it != groups. end ( ); ++it )
00862 sl << it.key();
00863
00864 return sl;
00865 };
00866
00868
00869
00871
00872 QStringList Config::allGroups()const {
00873 return groupList();
00874 }
00875
00886 long Config::timeStamp(const QString& name, Domain domain)
00887 {
00888 #ifdef Q_WS_WIN
00889
00890 QDateTime epoch;
00891 epoch.setTime_t(0);
00892 return epoch.secsTo(QFileInfo(Config::configFilename(name,domain)).lastModified());
00893 #else
00894 QString fn = Config::configFilename(name,domain);
00895 struct stat b;
00896 if (lstat( QFile::encodeName(fn).data(), &b ) == 0)
00897 return b.st_mtime;
00898 else
00899 return 0;
00900 #endif
00901 }
00902
00903
00911 void Config::removeGroup()
00912 {
00913 if ( git == groups.end() ) {
00914 qWarning( "no group set" );
00915 return;
00916 }
00917
00918 groups.remove(git.key());
00919 git = groups.end();
00920 changed = TRUE;
00921 }
00922
00930 void Config::removeGroup(const QString& g)
00931 {
00932 groups.remove(g);
00933 git = groups.end();
00934 }
00935
00936
00937
00952 void Config::writeEntry( const QString &key, const QStringList &lst )
00953 {
00954 QString s;
00955 for (QStringList::ConstIterator it=lst.begin(); it!=lst.end(); ++it) {
00956 QString el = *it;
00957 if ( el.isNull() ) {
00958 el = "^0";
00959 } else {
00960 el.replace(QRegExp("\\^"), "^^");
00961 }
00962 s+=el;
00963 s+="^e";
00964 }
00965 writeEntry(key, s);
00966 }
00967
00974 QStringList Config::readListEntry( const QString &key ) const
00975 {
00976 QString value = readEntry( key, QString::null );
00977 QStringList l;
00978 QString s;
00979 bool esc=FALSE;
00980 for (int i=0; i<(int)value.length(); i++) {
00981 if ( esc ) {
00982 if ( value[i] == 'e' ) {
00983 l.append(s);
00984 s="";
00985 } else if ( value[i] == '0' ) {
00986 s=QString::null;
00987 } else {
00988 s.append(value[i]);
00989 }
00990 esc = FALSE;
00991 } else if ( value[i] == '^' ) {
00992 esc = TRUE;
00993 } else {
00994 s.append(value[i]);
00995 if ( i == (int)value.length()-1 )
00996 l.append(s);
00997 }
00998 }
00999 return l;
01000 }
01001
01002 QString Config::readEntry( const QString &key, const QString &deflt ) const
01003 { return ((Config*)this)->readEntry(key,deflt); }
01004 QString Config::readEntryCrypt( const QString &key, const QString &deflt ) const
01005 { return ((Config*)this)->readEntryCrypt(key,deflt); }
01006 QString Config::readEntryDirect( const QString &key, const QString &deflt ) const
01007 { return ((Config*)this)->readEntryDirect(key,deflt); }
01008 int Config::readNumEntry( const QString &key, int deflt ) const
01009 { return ((Config*)this)->readNumEntry(key,deflt); }
01010 bool Config::readBoolEntry( const QString &key, bool deflt ) const
01011 { return ((Config*)this)->readBoolEntry(key,deflt); }
01012 QStringList Config::readListEntry( const QString &key, const QChar &sep ) const
01013 { return ((Config*)this)->readListEntry(key,sep); }