summaryrefslogtreecommitdiff
authorzecke <zecke>2004-12-26 13:32:10 (UTC)
committer zecke <zecke>2004-12-26 13:32:10 (UTC)
commitd8f38f36ad533f93d46c8ff883c6b42f15c96c28 (patch) (unidiff)
tree27f14def0ffc5d2b94cbd8aad7afbb66d532151d
parent4f5d6b7aff824a8a0f692cbab98b5cfe933de933 (diff)
downloadopie-d8f38f36ad533f93d46c8ff883c6b42f15c96c28.zip
opie-d8f38f36ad533f93d46c8ff883c6b42f15c96c28.tar.gz
opie-d8f38f36ad533f93d46c8ff883c6b42f15c96c28.tar.bz2
Config is now cached
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--library/config.cpp195
1 files changed, 195 insertions, 0 deletions
diff --git a/library/config.cpp b/library/config.cpp
index bdfcb3f..f68c336 100644
--- a/library/config.cpp
+++ b/library/config.cpp
@@ -1,98 +1,271 @@
1/********************************************************************** 1/**********************************************************************
2** Copyright (C) 2000,2004 Trolltech AS. All rights reserved. 2** Copyright (C) 2000,2004 Trolltech AS. All rights reserved.
3** 3**
4** This file is part of Qtopia Environment. 4** This file is part of Qtopia Environment.
5** 5**
6** This file may be distributed and/or modified under the terms of the 6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software 7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the 8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file. 9** packaging of this file.
10** 10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE 11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13** 13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information. 14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15** 15**
16** Contact info@trolltech.com if any conditions of this licensing are 16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you. 17** not clear to you.
18** 18**
19**********************************************************************/ 19**********************************************************************/
20 20
21#include <qdir.h> 21#include <qdir.h>
22#include <qmessagebox.h> 22#include <qmessagebox.h>
23#if QT_VERSION <= 230 && defined(QT_NO_CODECS) 23#if QT_VERSION <= 230 && defined(QT_NO_CODECS)
24#include <qtextcodec.h> 24#include <qtextcodec.h>
25#endif 25#endif
26#include <qtextstream.h> 26#include <qtextstream.h>
27 27
28#include <sys/stat.h> 28#include <sys/stat.h>
29#include <sys/types.h> 29#include <sys/types.h>
30#include <sys/time.h>
30#include <fcntl.h> 31#include <fcntl.h>
31#include <stdlib.h> 32#include <stdlib.h>
33#include <time.h>
32#include <unistd.h> 34#include <unistd.h>
33 35
34#define QTOPIA_INTERNAL_LANGLIST 36#define QTOPIA_INTERNAL_LANGLIST
35#include "config.h" 37#include "config.h"
36#include "global.h" 38#include "global.h"
37#include "qpeapplication.h" 39#include "qpeapplication.h"
38 40
39 41
40/* 42/*
41 * Internal Class 43 * Internal Class
42 */ 44 */
43class ConfigPrivate { 45class ConfigPrivate {
44public: 46public:
45 ConfigPrivate() : multilang(FALSE) {} 47 ConfigPrivate() : multilang(FALSE) {}
46 ConfigPrivate(const ConfigPrivate& o) : 48 ConfigPrivate(const ConfigPrivate& o) :
47 trfile(o.trfile), 49 trfile(o.trfile),
48 trcontext(o.trcontext), 50 trcontext(o.trcontext),
49 multilang(o.multilang) 51 multilang(o.multilang)
50 {} 52 {}
51 ConfigPrivate& operator=(const ConfigPrivate& o) 53 ConfigPrivate& operator=(const ConfigPrivate& o)
52 { 54 {
53 trfile = o.trfile; 55 trfile = o.trfile;
54 trcontext = o.trcontext; 56 trcontext = o.trcontext;
55 multilang = o.multilang; 57 multilang = o.multilang;
56 return *this; 58 return *this;
57 } 59 }
58 60
59 QString trfile; 61 QString trfile;
60 QCString trcontext; 62 QCString trcontext;
61 bool multilang; 63 bool multilang;
62}; 64};
63 65
64///////////////////////////////////////////////////////////////// 66/////////////////////////////////////////////////////////////////
65///////////////////////////////////////////////////////////////// 67/////////////////////////////////////////////////////////////////
66 68
69#ifndef Q_OS_WIN32
70
71//#define DEBUG_CONFIG_CACHE
72
73const int CONFIG_CACHE_SIZE = 8192;
74const int CONFIG_CACHE_TIMEOUT = 1000;
75
76class ConfigData
77{
78public:
79 ConfigData(const ConfigData& o) :
80 cfg(o.cfg),
81 priv(o.priv ? new ConfigPrivate(*o.priv) : 0),
82 mtime(o.mtime),
83 size(o.size),
84 used(o.used)
85 { }
86
87 ConfigData& operator=(const ConfigData& o)
88 {
89 cfg = o.cfg;
90 delete priv;
91 priv = o.priv ? new ConfigPrivate(*o.priv) : 0;
92 mtime = o.mtime;
93 size = o.size;
94 used = o.used;
95 return *this;
96 }
97
98 ConfigData() : priv(0) {}
99 ~ConfigData() { delete priv; }
100
101 ConfigGroupMap cfg;
102 ConfigPrivate *priv; // Owned by this object
103 time_t mtime;
104 unsigned int size;
105 struct timeval used;
106};
107
108class ConfigCache : public QObject
109{
110public:
111 ConfigCache();
112
113 void insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv);
114 bool find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv);
115 void remove(const QString &filename);
116
117protected:
118 void timerEvent(QTimerEvent *);
119
120private:
121 void removeLru();
122
123 QMap<QString, ConfigData> configData;
124 unsigned int totalsize;
125 int tid;
126};
127
128ConfigCache::ConfigCache() : QObject(), totalsize(0), tid(0)
129{
130}
131
132void ConfigCache::insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv)
133{
134 // use stat() rather than QFileInfo for speed.
135 struct stat sbuf;
136 stat(filename.local8Bit().data(), &sbuf);
137
138 if (sbuf.st_size < CONFIG_CACHE_SIZE/2) {
139 ConfigData data;
140 data.cfg = cfg;
141 data.priv = priv ? new ConfigPrivate(*priv) : 0;
142 data.mtime = sbuf.st_mtime;
143 data.size = sbuf.st_size;
144 gettimeofday(&data.used, 0);
145
146 remove(filename);
147 configData.insert(filename, data);
148
149 totalsize += data.size;
150#ifdef DEBUG_CONFIG_CACHE
151 qDebug("++++++ insert %s", filename.latin1());
152#endif
153 }
154
155 if (totalsize > (uint)CONFIG_CACHE_SIZE) {
156 // We'll delay deleting anything until later.
157 // This lets us grow quite large during some operations,
158 // but we'll be reduced to a decent size later.
159 // This works well with the main use case - app startup.
160 if (!tid)
161 tid = startTimer(CONFIG_CACHE_TIMEOUT);
162 }
163}
164
165bool ConfigCache::find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv)
166{
167 QMap<QString, ConfigData>::Iterator it = configData.find(filename);
168 if (it != configData.end()) {
169 ConfigData data = *it;
170 // use stat() rather than QFileInfo for speed.
171 struct stat sbuf;
172 stat(filename.local8Bit().data(), &sbuf);
173
174 if (data.mtime == sbuf.st_mtime && (int)data.size == sbuf.st_size) {
175 cfg = data.cfg;
176 delete priv;
177 priv = data.priv ? new ConfigPrivate(*data.priv) : 0;
178 gettimeofday(&data.used, 0);
179#ifdef DEBUG_CONFIG_CACHE
180 qDebug("******* Cache hit: %s", filename.latin1());
181#endif
182 return TRUE;
183 }
184 }
185
186#ifdef DEBUG_CONFIG_CACHE
187 qDebug("------- Cache miss: %s", filename.latin1());
188#endif
189
190 return FALSE;
191}
192
193void ConfigCache::remove(const QString &filename)
194{
195 QMap<QString, ConfigData>::Iterator it = configData.find(filename);
196 if (it != configData.end()) {
197 totalsize -= (*it).size;
198 configData.remove(it);
199 }
200}
201
202void ConfigCache::timerEvent(QTimerEvent *)
203{
204#ifdef DEBUG_CONFIG_CACHE
205 qDebug( "cache size: %d", totalsize);
206#endif
207 while (totalsize > (uint)CONFIG_CACHE_SIZE)
208 removeLru();
209 killTimer(tid);
210 tid = 0;
211}
212
213void ConfigCache::removeLru()
214{
215 QMap<QString, ConfigData>::Iterator it = configData.begin();
216 QMap<QString, ConfigData>::Iterator lru = it;
217 ++it;
218 for (; it != configData.end(); ++it) {
219 if ((*it).used.tv_sec < (*lru).used.tv_sec ||
220 ((*it).used.tv_sec == (*lru).used.tv_sec &&
221 (*it).used.tv_usec < (*lru).used.tv_usec))
222 lru = it;
223 }
224
225#ifdef DEBUG_CONFIG_CACHE
226 qDebug("Cache full, removing: %s", lru.key().latin1());
227#endif
228 totalsize -= (*lru).size;
229 configData.remove(lru);
230}
231
232static ConfigCache *qpe_configCache = 0;
233
234#endif /* Q_OS_WIN32 */
235
236
237// ==========================================================================
238
239
67/*! 240/*!
68 \internal 241 \internal
69*/ 242*/
70QString Config::configFilename(const QString& name, Domain d) 243QString Config::configFilename(const QString& name, Domain d)
71{ 244{
72 switch (d) { 245 switch (d) {
73 case File: 246 case File:
74 return name; 247 return name;
75 case User: { 248 case User: {
76 QDir dir = (QString(getenv("HOME")) + "/Settings"); 249 QDir dir = (QString(getenv("HOME")) + "/Settings");
77 if ( !dir.exists() ) 250 if ( !dir.exists() )
78 mkdir(dir.path().local8Bit(),0700); 251 mkdir(dir.path().local8Bit(),0700);
79 return dir.path() + "/" + name + ".conf"; 252 return dir.path() + "/" + name + ".conf";
80 } 253 }
81 } 254 }
82 return name; 255 return name;
83} 256}
84 257
85/* This cannot be made public because of binary compat issues */ 258/* This cannot be made public because of binary compat issues */
86void Config::read( QTextStream &s ) 259void Config::read( QTextStream &s )
87{ 260{
88#if QT_VERSION <= 230 && defined(QT_NO_CODECS) 261#if QT_VERSION <= 230 && defined(QT_NO_CODECS)
89 // The below should work, but doesn't in Qt 2.3.0 262 // The below should work, but doesn't in Qt 2.3.0
90 s.setCodec( QTextCodec::codecForMib( 106 ) ); 263 s.setCodec( QTextCodec::codecForMib( 106 ) );
91#else 264#else
92 s.setEncoding( QTextStream::UnicodeUTF8 ); 265 s.setEncoding( QTextStream::UnicodeUTF8 );
93#endif 266#endif
94 267
95 QStringList list = QStringList::split('\n', s.read() ); 268 QStringList list = QStringList::split('\n', s.read() );
96 269
97 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { 270 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
98 if ( !parse( *it ) ) { 271 if ( !parse( *it ) ) {
@@ -536,118 +709,140 @@ void Config::write( const QString &fn )
536 709
537 QString str; 710 QString str;
538 QCString cstr; 711 QCString cstr;
539 QMap< QString, ConfigGroup >::Iterator g_it = groups.begin(); 712 QMap< QString, ConfigGroup >::Iterator g_it = groups.begin();
540 713
541 for ( ; g_it != groups.end(); ++g_it ) { 714 for ( ; g_it != groups.end(); ++g_it ) {
542 str += "[" + g_it.key() + "]\n"; 715 str += "[" + g_it.key() + "]\n";
543 ConfigGroup::Iterator e_it = ( *g_it ).begin(); 716 ConfigGroup::Iterator e_it = ( *g_it ).begin();
544 for ( ; e_it != ( *g_it ).end(); ++e_it ) 717 for ( ; e_it != ( *g_it ).end(); ++e_it )
545 str += e_it.key() + " = " + *e_it + "\n"; 718 str += e_it.key() + " = " + *e_it + "\n";
546 } 719 }
547 cstr = str.utf8(); 720 cstr = str.utf8();
548 721
549 int total_length; 722 int total_length;
550 total_length = f.writeBlock( cstr.data(), cstr.length() ); 723 total_length = f.writeBlock( cstr.data(), cstr.length() );
551 if ( total_length != int(cstr.length()) ) { 724 if ( total_length != int(cstr.length()) ) {
552 QMessageBox::critical( 0, QObject::tr("Out of Space"), 725 QMessageBox::critical( 0, QObject::tr("Out of Space"),
553 QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") ); 726 QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") );
554 f.close(); 727 f.close();
555 QFile::remove( strNewFile ); 728 QFile::remove( strNewFile );
556 return; 729 return;
557 } 730 }
558 731
559 f.close(); 732 f.close();
560 // now rename the file... 733 // now rename the file...
561 if ( rename( strNewFile, filename ) < 0 ) { 734 if ( rename( strNewFile, filename ) < 0 ) {
562 qWarning( "problem renaming the file %s to %s", strNewFile.latin1(), 735 qWarning( "problem renaming the file %s to %s", strNewFile.latin1(),
563 filename.latin1() ); 736 filename.latin1() );
564 QFile::remove( strNewFile ); 737 QFile::remove( strNewFile );
565 return; 738 return;
566 } 739 }
567 740
741#ifndef Q_OS_WIN32
742 if (qpe_configCache)
743 qpe_configCache->insert(filename, groups, d);
744#endif
568 changed = FALSE; 745 changed = FALSE;
569} 746}
570 747
571/*! 748/*!
572 Returns whether the Config is in a valid state. 749 Returns whether the Config is in a valid state.
573*/ 750*/
574bool Config::isValid() const 751bool Config::isValid() const
575{ 752{
576 return groups.end() != git; 753 return groups.end() != git;
577} 754}
578 755
579/*! 756/*!
580 \internal 757 \internal
581*/ 758*/
582void Config::read() 759void Config::read()
583{ 760{
584 changed = FALSE; 761 changed = FALSE;
585 762
586 QString readFilename(filename); 763 QString readFilename(filename);
587 764
588 if ( !QFile::exists(filename) ) { 765 if ( !QFile::exists(filename) ) {
589 bool failed = TRUE; 766 bool failed = TRUE;
590 QFileInfo fi(filename); 767 QFileInfo fi(filename);
591 QString settingsDir = QDir::homeDirPath() + "/Settings"; 768 QString settingsDir = QDir::homeDirPath() + "/Settings";
592 if (fi.dirPath(TRUE) == settingsDir) { 769 if (fi.dirPath(TRUE) == settingsDir) {
593 // User setting - see if there is a default in $OPIEDIR/etc/default/ 770 // User setting - see if there is a default in $OPIEDIR/etc/default/
594 QString dftlFile = QPEApplication::qpeDir() + "etc/default/" + fi.fileName(); 771 QString dftlFile = QPEApplication::qpeDir() + "etc/default/" + fi.fileName();
595 if (QFile::exists(dftlFile)) { 772 if (QFile::exists(dftlFile)) {
596 readFilename = dftlFile; 773 readFilename = dftlFile;
597 failed = FALSE; 774 failed = FALSE;
598 } 775 }
599 } 776 }
600 if (failed) { 777 if (failed) {
601 git = groups.end(); 778 git = groups.end();
602 return; 779 return;
603 } 780 }
604 } 781 }
605 782
783#ifndef Q_OS_WIN32
784 if (!qpe_configCache)
785 qpe_configCache = new ConfigCache;
786
787 if (qpe_configCache->find(readFilename, groups, d)) {
788 if ( d && d->multilang ) {
789 QStringList l = Global::languageList();
790 lang = l[0];
791 glang = l[1];
792 }
793 git = groups.begin();
794 return;
795 }
796#endif
606 797
607 QFile f( readFilename ); 798 QFile f( readFilename );
608 if ( !f.open( IO_ReadOnly ) ) { 799 if ( !f.open( IO_ReadOnly ) ) {
609 git = groups.end(); 800 git = groups.end();
610 return; 801 return;
611 } 802 }
612 803
613 if (f.getch()!='[') { 804 if (f.getch()!='[') {
614 git = groups.end(); 805 git = groups.end();
615 return; 806 return;
616 } 807 }
617 f.ungetch('['); 808 f.ungetch('[');
618 809
619 QTextStream s( &f ); 810 QTextStream s( &f );
620 read( s ); 811 read( s );
621 f.close(); 812 f.close();
813
814#ifndef Q_OS_WIN32
815 qpe_configCache->insert(readFilename, groups, d);
816#endif
622} 817}
623 818
624/*! 819/*!
625 \internal 820 \internal
626*/ 821*/
627bool Config::parse( const QString &l ) 822bool Config::parse( const QString &l )
628{ 823{
629 QString line = l.stripWhiteSpace(); 824 QString line = l.stripWhiteSpace();
630 if ( line[ 0 ] == QChar( '[' ) ) { 825 if ( line[ 0 ] == QChar( '[' ) ) {
631 QString gname = line; 826 QString gname = line;
632 gname = gname.remove( 0, 1 ); 827 gname = gname.remove( 0, 1 );
633 if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) ) 828 if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) )
634 gname = gname.remove( gname.length() - 1, 1 ); 829 gname = gname.remove( gname.length() - 1, 1 );
635 git = groups.insert( gname, ConfigGroup() ); 830 git = groups.insert( gname, ConfigGroup() );
636 } else if ( !line.isEmpty() ) { 831 } else if ( !line.isEmpty() ) {
637 if ( git == groups.end() ) 832 if ( git == groups.end() )
638 return FALSE; 833 return FALSE;
639 int eq = line.find( '=' ); 834 int eq = line.find( '=' );
640 if ( eq == -1 ) 835 if ( eq == -1 )
641 return FALSE; 836 return FALSE;
642 QString key = line.left(eq).stripWhiteSpace(); 837 QString key = line.left(eq).stripWhiteSpace();
643 QString value = line.mid(eq+1).stripWhiteSpace(); 838 QString value = line.mid(eq+1).stripWhiteSpace();
644 839
645 if ( git.key() == "Translation" ) { 840 if ( git.key() == "Translation" ) {
646 if ( key == "File" ) { 841 if ( key == "File" ) {
647 if ( !d ) 842 if ( !d )
648 d = new ConfigPrivate; 843 d = new ConfigPrivate;
649 d->trfile = value; 844 d->trfile = value;
650 } else if ( key == "Context" ) { 845 } else if ( key == "Context" ) {
651 if ( !d ) 846 if ( !d )
652 d = new ConfigPrivate; 847 d = new ConfigPrivate;
653 d->trcontext = value.latin1(); 848 d->trcontext = value.latin1();