summaryrefslogtreecommitdiff
Unidiff
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,130 +1,303 @@
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 ) ) {
99 git = groups.end(); 272 git = groups.end();
100 return; 273 return;
101 } 274 }
102 } 275 }
103} 276}
104 277
105/*! 278/*!
106 \class Config config.h 279 \class Config config.h
107 \brief The Config class provides for saving application cofniguration state. 280 \brief The Config class provides for saving application cofniguration state.
108 281
109 You should keep a Config in existence only while you do not want others 282 You should keep a Config in existence only while you do not want others
110 to be able to change the state. There is no locking currently, but there 283 to be able to change the state. There is no locking currently, but there
111 may be in the future. 284 may be in the future.
112*/ 285*/
113 286
114/*! 287/*!
115 \enum Config::ConfigGroup 288 \enum Config::ConfigGroup
116 \internal 289 \internal
117*/ 290*/
118 291
119/*! 292/*!
120 \enum Config::Domain 293 \enum Config::Domain
121 294
122 \value File 295 \value File
123 \value User 296 \value User
124 297
125 See Config for details. 298 See Config for details.
126*/ 299*/
127 300
128/*! 301/*!
129 Constructs a config that will load or create a configuration with the 302 Constructs a config that will load or create a configuration with the
130 given \a name in the given \a domain. 303 given \a name in the given \a domain.
@@ -504,182 +677,204 @@ QStringList Config::readListEntry( const QString &key, const QChar &sep )
504 677
505/*! 678/*!
506 Removes all entries from the current group. 679 Removes all entries from the current group.
507*/ 680*/
508void Config::clearGroup() 681void Config::clearGroup()
509{ 682{
510 if ( git == groups.end() ) { 683 if ( git == groups.end() ) {
511 qWarning( "no group set" ); 684 qWarning( "no group set" );
512 return; 685 return;
513 } 686 }
514 if ( !(*git).isEmpty() ) { 687 if ( !(*git).isEmpty() ) {
515 ( *git ).clear(); 688 ( *git ).clear();
516 changed = TRUE; 689 changed = TRUE;
517 } 690 }
518} 691}
519 692
520/*! 693/*!
521 \internal 694 \internal
522*/ 695*/
523void Config::write( const QString &fn ) 696void Config::write( const QString &fn )
524{ 697{
525 QString strNewFile; 698 QString strNewFile;
526 if ( !fn.isEmpty() ) 699 if ( !fn.isEmpty() )
527 filename = fn; 700 filename = fn;
528 strNewFile = filename + ".new"; 701 strNewFile = filename + ".new";
529 702
530 QFile f( strNewFile ); 703 QFile f( strNewFile );
531 if ( !f.open( IO_WriteOnly|IO_Raw ) ) { 704 if ( !f.open( IO_WriteOnly|IO_Raw ) ) {
532 qWarning( "could not open for writing `%s'", strNewFile.latin1() ); 705 qWarning( "could not open for writing `%s'", strNewFile.latin1() );
533 git = groups.end(); 706 git = groups.end();
534 return; 707 return;
535 } 708 }
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();
654 } else if ( key.startsWith("Comment") ) { 849 } else if ( key.startsWith("Comment") ) {
655 return TRUE; // ignore comment for ts file 850 return TRUE; // ignore comment for ts file
656 } else { 851 } else {
657 return FALSE; // Unrecognized 852 return FALSE; // Unrecognized
658 } 853 }
659 } 854 }
660 855
661 int kl = key.length(); 856 int kl = key.length();
662 if ( kl > 1 && key[kl-1] == ']' && key[kl-2] != '[' ) { 857 if ( kl > 1 && key[kl-1] == ']' && key[kl-2] != '[' ) {
663 // Old-style translation (inefficient) 858 // Old-style translation (inefficient)
664 if ( !d ) 859 if ( !d )
665 d = new ConfigPrivate; 860 d = new ConfigPrivate;
666 if ( !d->multilang ) { 861 if ( !d->multilang ) {
667 QStringList l = Global::languageList(); 862 QStringList l = Global::languageList();
668 lang = l[0]; 863 lang = l[0];
669 glang = l[1]; 864 glang = l[1];
670 d->multilang = TRUE; 865 d->multilang = TRUE;
671 } 866 }
672 } 867 }
673 868
674 ( *git ).insert( key, value ); 869 ( *git ).insert( key, value );
675 } 870 }
676 return TRUE; 871 return TRUE;
677} 872}
678 873
679 874
680 875
681bool Config::hasGroup( const QString& name )const { 876bool Config::hasGroup( const QString& name )const {
682 return ( groups. find ( name ) != groups. end ( )); 877 return ( groups. find ( name ) != groups. end ( ));
683}; 878};
684 879
685QStringList Config::groupList()const { 880QStringList Config::groupList()const {