summaryrefslogtreecommitdiff
path: root/library/config.cpp
Unidiff
Diffstat (limited to 'library/config.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--library/config.cpp264
1 files changed, 122 insertions, 142 deletions
diff --git a/library/config.cpp b/library/config.cpp
index f68c336..e9cae4c 100644
--- a/library/config.cpp
+++ b/library/config.cpp
@@ -48,230 +48,210 @@ public:
48 ConfigPrivate(const ConfigPrivate& o) : 48 ConfigPrivate(const ConfigPrivate& o) :
49 trfile(o.trfile), 49 trfile(o.trfile),
50 trcontext(o.trcontext), 50 trcontext(o.trcontext),
51 multilang(o.multilang) 51 multilang(o.multilang)
52 {} 52 {}
53 ConfigPrivate& operator=(const ConfigPrivate& o) 53 ConfigPrivate& operator=(const ConfigPrivate& o)
54 { 54 {
55 trfile = o.trfile; 55 trfile = o.trfile;
56 trcontext = o.trcontext; 56 trcontext = o.trcontext;
57 multilang = o.multilang; 57 multilang = o.multilang;
58 return *this; 58 return *this;
59 } 59 }
60 60
61 QString trfile; 61 QString trfile;
62 QCString trcontext; 62 QCString trcontext;
63 bool multilang; 63 bool multilang;
64}; 64};
65 65
66///////////////////////////////////////////////////////////////// 66/////////////////////////////////////////////////////////////////
67///////////////////////////////////////////////////////////////// 67/////////////////////////////////////////////////////////////////
68 68
69#ifndef Q_OS_WIN32 69#ifndef Q_OS_WIN32
70 70
71//#define DEBUG_CONFIG_CACHE 71//#define DEBUG_CONFIG_CACHE
72
73const int CONFIG_CACHE_SIZE = 8192;
74const int CONFIG_CACHE_TIMEOUT = 1000;
75
76class ConfigData 72class ConfigData
77{ 73{
78public: 74public:
79 ConfigData(const ConfigData& o) : 75 ConfigData() {}
80 cfg(o.cfg), 76 ConfigData( const ConfigGroupMap& cf, const ConfigPrivate& pri,
81 priv(o.priv ? new ConfigPrivate(*o.priv) : 0), 77 struct stat sbuf )
82 mtime(o.mtime), 78 : cfg( cf ), priv( pri ), mtime( sbuf.st_mtime ),
83 size(o.size), 79 size( sbuf.st_size )
84 used(o.used) 80 {
85 { } 81 gettimeofday(&used, 0 );
86 82 }
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 83
101 ConfigGroupMap cfg; 84 ConfigGroupMap cfg;
102 ConfigPrivate *priv; // Owned by this object 85 ConfigPrivate priv; // Owned by this object
103 time_t mtime; 86 time_t mtime;
104 unsigned int size; 87 unsigned int size;
105 struct timeval used; 88 struct timeval used;
106}; 89};
107 90
108class ConfigCache : public QObject
109{
110public:
111 ConfigCache();
112 91
113 void insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv); 92class ConfigCache : public QObject {
114 bool find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv); 93public:
115 void remove(const QString &filename); 94 static ConfigCache* instance();
116 95
96 void insert( const QString& fileName, const ConfigGroupMap& cfg,
97 const ConfigPrivate *priv );
98 bool find(const QString& fileName, ConfigGroupMap& cfg,
99 ConfigPrivate** priv );
117protected: 100protected:
118 void timerEvent(QTimerEvent *); 101 void timerEvent( QTimerEvent* );
119 102
120private: 103private:
104 ConfigCache();
105 void remove( const QString& fileName );
121 void removeLru(); 106 void removeLru();
122 107
123 QMap<QString, ConfigData> configData; 108private:
124 unsigned int totalsize; 109 QMap<QString, ConfigData> m_cached;
125 int tid; 110 unsigned int m_totalSize;
111 int m_tid;
112private:
113 static ConfigCache* m_inst;
114 static const unsigned int CONFIG_CACHE_SIZE = 8192;
115 static const unsigned int CONFIG_CACHE_TIMEOUT = 1000;
126}; 116};
127 117
128ConfigCache::ConfigCache() : QObject(), totalsize(0), tid(0) 118ConfigCache* ConfigCache::m_inst = 0;
129{ 119/*
130} 120 * get destroyed when qApp gets destroyed
131 121 */
132void ConfigCache::insert(const QString &filename, const ConfigGroupMap &cfg, const ConfigPrivate* priv) 122ConfigCache::ConfigCache() : QObject( qApp ), m_totalSize( 0 ), m_tid( 0 ) {}
133{ 123ConfigCache* ConfigCache::instance() {
134 // use stat() rather than QFileInfo for speed. 124 if ( !m_inst )
135 struct stat sbuf; 125 m_inst = new ConfigCache();
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 126
155 if (totalsize > (uint)CONFIG_CACHE_SIZE) { 127 return m_inst;
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} 128}
164 129
165bool ConfigCache::find(const QString &filename, ConfigGroupMap &cfg, ConfigPrivate*& priv) 130void ConfigCache::remove( const QString& fileName ) {
166{ 131 QMap<QString, ConfigData>::Iterator it = m_cached.find( fileName );
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 132
190 return FALSE; 133 if ( it == m_cached.end() )
191} 134 return;
192 135
193void ConfigCache::remove(const QString &filename) 136 m_totalSize -= (*it).size;
194{ 137 m_cached.remove( it );
195 QMap<QString, ConfigData>::Iterator it = configData.find(filename);
196 if (it != configData.end()) {
197 totalsize -= (*it).size;
198 configData.remove(it);
199 }
200} 138}
201 139
202void ConfigCache::timerEvent(QTimerEvent *) 140void ConfigCache::removeLru() {
203{ 141 QMap<QString, ConfigData>::Iterator it = m_cached.begin();
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; 142 QMap<QString, ConfigData>::Iterator lru = it;
217 ++it; 143 ++it;
218 for (; it != configData.end(); ++it) { 144 for (; it != m_cached.end(); ++it)
219 if ((*it).used.tv_sec < (*lru).used.tv_sec || 145 if ((*it).used.tv_sec < (*lru).used.tv_sec ||
220 ((*it).used.tv_sec == (*lru).used.tv_sec && 146 ((*it).used.tv_sec == (*lru).used.tv_sec &&
221 (*it).used.tv_usec < (*lru).used.tv_usec)) 147 (*it).used.tv_usec < (*lru).used.tv_usec))
222 lru = it; 148 lru = it;
223 }
224 149
225#ifdef DEBUG_CONFIG_CACHE 150 qWarning( "Removing item" );
226 qDebug("Cache full, removing: %s", lru.key().latin1()); 151 m_totalSize -= (*lru).size;
227#endif 152 m_cached.remove(lru);
228 totalsize -= (*lru).size; 153}
229 configData.remove(lru); 154
155void ConfigCache::timerEvent( QTimerEvent* ) {
156 while ( m_totalSize > CONFIG_CACHE_SIZE )
157 removeLru();
158
159 killTimer(m_tid);
160 m_tid = 0;
230} 161}
231 162
232static ConfigCache *qpe_configCache = 0; 163void ConfigCache::insert( const QString& fileName, const ConfigGroupMap& cfg,
164 const ConfigPrivate* _priv ) {
233 165
234#endif /* Q_OS_WIN32 */
235 166
167 struct stat sbuf;
168 ::stat( QFile::encodeName(fileName), &sbuf );
169 if ( static_cast<unsigned int>(sbuf.st_size) >= CONFIG_CACHE_SIZE>>1)
170 return;
236 171
237// ========================================================================== 172 /*
173 * remove the old version and use the new one
174 */
175 ConfigPrivate priv = _priv ? *_priv : ConfigPrivate();
176 ConfigData data( cfg, priv, sbuf );
177 m_totalSize += data.size;
178
179 remove( fileName );
180 m_cached.insert( fileName, data );
181
182 /*
183 * we've overcommited allocation, let us clean up
184 * soon
185 */
186 if ( m_totalSize >= CONFIG_CACHE_SIZE )
187 if ( !m_tid )
188 m_tid = startTimer(CONFIG_CACHE_TIMEOUT);
189}
190
191bool ConfigCache::find( const QString& fileName, ConfigGroupMap& cfg,
192 ConfigPrivate **ppriv ) {
193 QMap<QString, ConfigData>::Iterator it = m_cached.find(fileName);
194 if (it != m_cached.end()) {
195 ConfigData &data = *it;
196 struct stat sbuf;
197 ::stat(QFile::encodeName( fileName ), &sbuf);
198
199 if (data.mtime == sbuf.st_mtime && (int)data.size == sbuf.st_size) {
200 cfg = data.cfg;
201
202 /*
203 * null pointer
204 */
205 if ( *ppriv == 0 )
206 *ppriv = new ConfigPrivate( data.priv );
207 **ppriv = data.priv;
208 gettimeofday(&data.used, 0);
209
210 return true;
211 }
212 }
213
214 return false;
215}
216
217#endif
238 218
239 219
240/*! 220/*!
241 \internal 221 \internal
242*/ 222*/
243QString Config::configFilename(const QString& name, Domain d) 223QString Config::configFilename(const QString& name, Domain d)
244{ 224{
245 switch (d) { 225 switch (d) {
246 case File: 226 case File:
247 return name; 227 return name;
248 case User: { 228 case User: {
249 QDir dir = (QString(getenv("HOME")) + "/Settings"); 229 QDir dir = (QString(getenv("HOME")) + "/Settings");
250 if ( !dir.exists() ) 230 if ( !dir.exists() )
251 mkdir(dir.path().local8Bit(),0700); 231 mkdir(dir.path().local8Bit(),0700);
252 return dir.path() + "/" + name + ".conf"; 232 return dir.path() + "/" + name + ".conf";
253 } 233 }
254 } 234 }
255 return name; 235 return name;
256} 236}
257 237
258/* This cannot be made public because of binary compat issues */ 238/* This cannot be made public because of binary compat issues */
259void Config::read( QTextStream &s ) 239void Config::read( QTextStream &s )
260{ 240{
261#if QT_VERSION <= 230 && defined(QT_NO_CODECS) 241#if QT_VERSION <= 230 && defined(QT_NO_CODECS)
262 // The below should work, but doesn't in Qt 2.3.0 242 // The below should work, but doesn't in Qt 2.3.0
263 s.setCodec( QTextCodec::codecForMib( 106 ) ); 243 s.setCodec( QTextCodec::codecForMib( 106 ) );
264#else 244#else
265 s.setEncoding( QTextStream::UnicodeUTF8 ); 245 s.setEncoding( QTextStream::UnicodeUTF8 );
266#endif 246#endif
267 247
268 QStringList list = QStringList::split('\n', s.read() ); 248 QStringList list = QStringList::split('\n', s.read() );
269 249
270 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { 250 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
271 if ( !parse( *it ) ) { 251 if ( !parse( *it ) ) {
272 git = groups.end(); 252 git = groups.end();
273 return; 253 return;
274 } 254 }
275 } 255 }
276} 256}
277 257
@@ -674,166 +654,166 @@ QStringList Config::readListEntry( const QString &key, const QChar &sep )
674 else 654 else
675 return QStringList::split( sep, s ); 655 return QStringList::split( sep, s );
676} 656}
677 657
678/*! 658/*!
679 Removes all entries from the current group. 659 Removes all entries from the current group.
680*/ 660*/
681void Config::clearGroup() 661void Config::clearGroup()
682{ 662{
683 if ( git == groups.end() ) { 663 if ( git == groups.end() ) {
684 qWarning( "no group set" ); 664 qWarning( "no group set" );
685 return; 665 return;
686 } 666 }
687 if ( !(*git).isEmpty() ) { 667 if ( !(*git).isEmpty() ) {
688 ( *git ).clear(); 668 ( *git ).clear();
689 changed = TRUE; 669 changed = TRUE;
690 } 670 }
691} 671}
692 672
693/*! 673/*!
694 \internal 674 \internal
695*/ 675*/
696void Config::write( const QString &fn ) 676void Config::write( const QString &fn )
697{ 677{
678 QString oldGroup = git.key();
679
698 QString strNewFile; 680 QString strNewFile;
699 if ( !fn.isEmpty() ) 681 if ( !fn.isEmpty() )
700 filename = fn; 682 filename = fn;
701 strNewFile = filename + ".new"; 683 strNewFile = filename + ".new";
702 684
703 QFile f( strNewFile ); 685 QFile f( strNewFile );
704 if ( !f.open( IO_WriteOnly|IO_Raw ) ) { 686 if ( !f.open( IO_WriteOnly|IO_Raw ) ) {
705 qWarning( "could not open for writing `%s'", strNewFile.latin1() ); 687 qWarning( "could not open for writing `%s'", strNewFile.latin1() );
706 git = groups.end(); 688 git = groups.end();
707 return; 689 return;
708 } 690 }
709 691
710 QString str; 692 QString str;
711 QCString cstr; 693 QCString cstr;
712 QMap< QString, ConfigGroup >::Iterator g_it = groups.begin(); 694 QMap< QString, ConfigGroup >::Iterator g_it = groups.begin();
713 695
714 for ( ; g_it != groups.end(); ++g_it ) { 696 for ( ; g_it != groups.end(); ++g_it ) {
715 str += "[" + g_it.key() + "]\n"; 697 str += "[" + g_it.key() + "]\n";
716 ConfigGroup::Iterator e_it = ( *g_it ).begin(); 698 ConfigGroup::Iterator e_it = ( *g_it ).begin();
717 for ( ; e_it != ( *g_it ).end(); ++e_it ) 699 for ( ; e_it != ( *g_it ).end(); ++e_it )
718 str += e_it.key() + " = " + *e_it + "\n"; 700 str += e_it.key() + " = " + *e_it + "\n";
719 } 701 }
720 cstr = str.utf8(); 702 cstr = str.utf8();
721 703
722 int total_length; 704 int total_length;
723 total_length = f.writeBlock( cstr.data(), cstr.length() ); 705 total_length = f.writeBlock( cstr.data(), cstr.length() );
724 if ( total_length != int(cstr.length()) ) { 706 if ( total_length != int(cstr.length()) ) {
725 QMessageBox::critical( 0, QObject::tr("Out of Space"), 707 QMessageBox::critical( 0, QObject::tr("Out of Space"),
726 QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") ); 708 QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") );
727 f.close(); 709 f.close();
728 QFile::remove( strNewFile ); 710 QFile::remove( strNewFile );
729 return; 711 return;
730 } 712 }
731 713
732 f.close(); 714 f.close();
733 // now rename the file... 715 // now rename the file...
734 if ( rename( strNewFile, filename ) < 0 ) { 716 if ( rename( strNewFile, filename ) < 0 ) {
735 qWarning( "problem renaming the file %s to %s", strNewFile.latin1(), 717 qWarning( "problem renaming the file %s to %s", strNewFile.latin1(),
736 filename.latin1() ); 718 filename.latin1() );
737 QFile::remove( strNewFile ); 719 QFile::remove( strNewFile );
738 return; 720 return;
739 } 721 }
740 722
741#ifndef Q_OS_WIN32 723#ifndef Q_OS_WIN32
742 if (qpe_configCache) 724 ConfigCache::instance()->insert( filename, groups, d );
743 qpe_configCache->insert(filename, groups, d); 725 setGroup( oldGroup );
744#endif 726#endif
745 changed = FALSE; 727 changed = FALSE;
746} 728}
747 729
748/*! 730/*!
749 Returns whether the Config is in a valid state. 731 Returns whether the Config is in a valid state.
750*/ 732*/
751bool Config::isValid() const 733bool Config::isValid() const
752{ 734{
753 return groups.end() != git; 735 return groups.end() != git;
754} 736}
755 737
756/*! 738/*!
757 \internal 739 \internal
758*/ 740*/
759void Config::read() 741void Config::read()
760{ 742{
761 changed = FALSE; 743 changed = FALSE;
762 744
763 QString readFilename(filename); 745 QString readFilename(filename);
764 746
765 if ( !QFile::exists(filename) ) { 747 if ( !QFile::exists(filename) ) {
766 bool failed = TRUE; 748 bool failed = TRUE;
767 QFileInfo fi(filename); 749 QFileInfo fi(filename);
768 QString settingsDir = QDir::homeDirPath() + "/Settings"; 750 QString settingsDir = QDir::homeDirPath() + "/Settings";
769 if (fi.dirPath(TRUE) == settingsDir) { 751 if (fi.dirPath(TRUE) == settingsDir) {
770 // User setting - see if there is a default in $OPIEDIR/etc/default/ 752 // User setting - see if there is a default in $OPIEDIR/etc/default/
771 QString dftlFile = QPEApplication::qpeDir() + "etc/default/" + fi.fileName(); 753 QString dftlFile = QPEApplication::qpeDir() + "etc/default/" + fi.fileName();
772 if (QFile::exists(dftlFile)) { 754 if (QFile::exists(dftlFile)) {
773 readFilename = dftlFile; 755 readFilename = dftlFile;
774 failed = FALSE; 756 failed = FALSE;
775 } 757 }
776 } 758 }
777 if (failed) { 759 if (failed) {
778 git = groups.end(); 760 git = groups.end();
779 return; 761 return;
780 } 762 }
781 } 763 }
782 764
783#ifndef Q_OS_WIN32 765#ifndef Q_OS_WIN32
784 if (!qpe_configCache)
785 qpe_configCache = new ConfigCache;
786 766
787 if (qpe_configCache->find(readFilename, groups, d)) { 767 if (ConfigCache::instance()->find(readFilename, groups, &d)) {
788 if ( d && d->multilang ) { 768 if ( d && d->multilang ) {
789 QStringList l = Global::languageList(); 769 QStringList l = Global::languageList();
790 lang = l[0]; 770 lang = l[0];
791 glang = l[1]; 771 glang = l[1];
792 } 772 }
793 git = groups.begin(); 773 git = groups.begin();
794 return; 774 return;
795 } 775 }
796#endif 776#endif
797 777
798 QFile f( readFilename ); 778 QFile f( readFilename );
799 if ( !f.open( IO_ReadOnly ) ) { 779 if ( !f.open( IO_ReadOnly ) ) {
800 git = groups.end(); 780 git = groups.end();
801 return; 781 return;
802 } 782 }
803 783
804 if (f.getch()!='[') { 784 if (f.getch()!='[') {
805 git = groups.end(); 785 git = groups.end();
806 return; 786 return;
807 } 787 }
808 f.ungetch('['); 788 f.ungetch('[');
809 789
810 QTextStream s( &f ); 790 QTextStream s( &f );
811 read( s ); 791 read( s );
812 f.close(); 792 f.close();
813 793
814#ifndef Q_OS_WIN32 794#ifndef Q_OS_WIN32
815 qpe_configCache->insert(readFilename, groups, d); 795 ConfigCache::instance()->insert(readFilename, groups, d);
816#endif 796#endif
817} 797}
818 798
819/*! 799/*!
820 \internal 800 \internal
821*/ 801*/
822bool Config::parse( const QString &l ) 802bool Config::parse( const QString &l )
823{ 803{
824 QString line = l.stripWhiteSpace(); 804 QString line = l.stripWhiteSpace();
825 if ( line[ 0 ] == QChar( '[' ) ) { 805 if ( line[ 0 ] == QChar( '[' ) ) {
826 QString gname = line; 806 QString gname = line;
827 gname = gname.remove( 0, 1 ); 807 gname = gname.remove( 0, 1 );
828 if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) ) 808 if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) )
829 gname = gname.remove( gname.length() - 1, 1 ); 809 gname = gname.remove( gname.length() - 1, 1 );
830 git = groups.insert( gname, ConfigGroup() ); 810 git = groups.insert( gname, ConfigGroup() );
831 } else if ( !line.isEmpty() ) { 811 } else if ( !line.isEmpty() ) {
832 if ( git == groups.end() ) 812 if ( git == groups.end() )
833 return FALSE; 813 return FALSE;
834 int eq = line.find( '=' ); 814 int eq = line.find( '=' );
835 if ( eq == -1 ) 815 if ( eq == -1 )
836 return FALSE; 816 return FALSE;
837 QString key = line.left(eq).stripWhiteSpace(); 817 QString key = line.left(eq).stripWhiteSpace();
838 QString value = line.mid(eq+1).stripWhiteSpace(); 818 QString value = line.mid(eq+1).stripWhiteSpace();
839 819