summaryrefslogtreecommitdiff
path: root/library/config.cpp
Unidiff
Diffstat (limited to 'library/config.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--library/config.cpp557
1 files changed, 557 insertions, 0 deletions
diff --git a/library/config.cpp b/library/config.cpp
new file mode 100644
index 0000000..9634571
--- a/dev/null
+++ b/library/config.cpp
@@ -0,0 +1,557 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
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
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
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.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <qdir.h>
22#include <qfile.h>
23#include <qfileinfo.h>
24#include <qmessagebox.h>
25#if QT_VERSION <= 230 && defined(QT_NO_CODECS)
26#include <qtextcodec.h>
27#endif
28#include <qtextstream.h>
29
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <fcntl.h>
33#include <stdlib.h>
34#include <unistd.h>
35
36#include "config.h"
37
38
39/*!
40 \internal
41*/
42QString Config::configFilename(const QString& name, Domain d)
43{
44 switch (d) {
45 case File:
46 return name;
47 case User: {
48 QDir dir = (QString(getenv("HOME")) + "/Settings");
49 if ( !dir.exists() )
50 mkdir(dir.path().local8Bit(),0700);
51 return dir.path() + "/" + name + ".conf";
52 }
53 }
54 return name;
55}
56
57/*!
58 \class Config config.h
59 \brief The Config class provides for saving application cofniguration state.
60
61 You should keep a Config in existence only while you do not want others
62 to be able to change the state. There is no locking currently, but there
63 may be in the future.
64*/
65
66/*!
67 \enum Config::ConfigGroup
68 \internal
69*/
70
71/*!
72 \enum Config::Domain
73
74 \value File
75 \value User
76
77 See Config for details.
78*/
79
80/*!
81 Constructs a config that will load or create a configuration with the
82 given \a name in the given \a domain.
83
84 You must call setGroup() before doing much else with the Config.
85
86 In the default Domain, \e User,
87 the configuration is user-specific. \a name should not contain "/" in
88 this case, and in general should be the name of the C++ class that is
89 primarily responsible for maintaining the configuration.
90
91 In the File Domain, \a name is an absolute filename.
92*/
93Config::Config( const QString &name, Domain domain )
94 : filename( configFilename(name,domain) )
95{
96 git = groups.end();
97 read();
98
99 lang = getenv("LANG");
100 int i = lang.find(".");
101 if ( i > 0 )
102 lang = lang.left( i );
103 i = lang.find( "_" );
104 if ( i > 0 )
105 glang = lang.left(i);
106}
107
108/*!
109 Writes any changes to disk and destroys the in-memory object.
110*/
111Config::~Config()
112{
113 if ( changed )
114 write();
115}
116
117/*!
118 Returns whether the current group has an entry called \a key.
119*/
120bool Config::hasKey( const QString &key ) const
121{
122 if ( groups.end() == git )
123 return FALSE;
124 ConfigGroup::ConstIterator it = ( *git ).find( key );
125 return it != ( *git ).end();
126}
127
128/*!
129 Sets the current group for subsequent reading and writing of
130 entries to \a gname. Grouping allows the application to partition the namespace.
131
132 This function must be called prior to any reading or writing
133 of entries.
134
135 The \a gname must not be empty.
136*/
137void Config::setGroup( const QString &gname )
138{
139 QMap< QString, ConfigGroup>::Iterator it = groups.find( gname );
140 if ( it == groups.end() ) {
141 git = groups.insert( gname, ConfigGroup() );
142 changed = TRUE;
143 return;
144 }
145 git = it;
146}
147
148/*!
149 Writes a (\a key, \a value) entry to the current group.
150
151 \sa readEntry()
152*/
153void Config::writeEntry( const QString &key, const char* value )
154{
155 writeEntry(key,QString(value));
156}
157
158/*!
159 Writes a (\a key, \a value) entry to the current group.
160
161 \sa readEntry()
162*/
163void Config::writeEntry( const QString &key, const QString &value )
164{
165 if ( git == groups.end() ) {
166 qWarning( "no group set" );
167 return;
168 }
169 if ( (*git)[key] != value ) {
170 ( *git ).insert( key, value );
171 changed = TRUE;
172 }
173}
174
175/*
176 Note that the degree of protection offered by the encryption here is
177 only sufficient to avoid the most casual observation of the configuration
178 files. People with access to the files can write down the contents and
179 decrypt it using this source code.
180
181 Conceivably, and at some burden to the user, this encryption could
182 be improved.
183*/
184static QString encipher(const QString& plain)
185{
186 // mainly, we make it long
187 QString cipher;
188 int mix=28730492;
189 for (int i=0; i<(int)plain.length(); i++) {
190 int u = plain[i].unicode();
191 int c = u ^ mix;
192 QString x = QString::number(c,36);
193 cipher.append(QChar('a'+x.length()));
194 cipher.append(x);
195 mix *= u;
196 }
197 return cipher;
198}
199
200static QString decipher(const QString& cipher)
201{
202 QString plain;
203 int mix=28730492;
204 for (int i=0; i<(int)cipher.length();) {
205 int l = cipher[i].unicode()-'a';
206 QString x = cipher.mid(i+1,l); i+=l+1;
207 int u = x.toInt(0,36) ^ mix;
208 plain.append(QChar(u));
209 mix *= u;
210 }
211 return plain;
212}
213
214/*!
215 Writes an encrypted (\a key, \a value) entry to the current group.
216
217 Note that the degree of protection offered by the encryption is
218 only sufficient to avoid the most casual observation of the configuration
219 files.
220
221 \sa readEntry()
222*/
223void Config::writeEntryCrypt( const QString &key, const QString &value )
224{
225 if ( git == groups.end() ) {
226 qWarning( "no group set" );
227 return;
228 }
229 QString evalue = encipher(value);
230 if ( (*git)[key] != evalue ) {
231 ( *git ).insert( key, evalue );
232 changed = TRUE;
233 }
234}
235
236/*!
237 Writes a (\a key, \a num) entry to the current group.
238
239 \sa readNumEntry()
240*/
241void Config::writeEntry( const QString &key, int num )
242{
243 QString s;
244 s.setNum( num );
245 writeEntry( key, s );
246}
247
248#ifdef Q_HAS_BOOL_TYPE
249/*!
250 Writes a (\a key, \a b) entry to the current group. This is equivalent
251 to writing a 0 or 1 as an integer entry.
252
253 \sa readBoolEntry()
254*/
255void Config::writeEntry( const QString &key, bool b )
256{
257 QString s;
258 s.setNum( ( int )b );
259 writeEntry( key, s );
260}
261#endif
262
263/*!
264 Writes a (\a key, \a lst) entry to the current group. The list
265 is separated by \a sep, so the strings must not contain that character.
266
267 \sa readListEntry()
268*/
269void Config::writeEntry( const QString &key, const QStringList &lst, const QChar &sep )
270{
271 QString s;
272 QStringList::ConstIterator it = lst.begin();
273 for ( ; it != lst.end(); ++it )
274 s += *it + sep;
275 writeEntry( key, s );
276}
277
278/*!
279 Removes the \a key entry from the current group. Does nothing if
280 there is no such entry.
281*/
282
283void Config::removeEntry( const QString &key )
284{
285 if ( git == groups.end() ) {
286 qWarning( "no group set" );
287 return;
288 }
289 ( *git ).remove( key );
290 changed = TRUE;
291}
292
293/*!
294 \fn bool Config::operator == ( const Config & other ) const
295
296 Tests for equality with \a other. Config objects are equal if they refer to the same filename.
297*/
298
299/*!
300 \fn bool Config::operator != ( const Config & other ) const
301
302 Tests for inequality with \a other. Config objects are equal if they refer to the same filename.
303*/
304
305/*!
306 \fn QString Config::readEntry( const QString &key, const QString &deflt ) const
307
308 Reads a string entry stored with \a key, defaulting to \a deflt if there is no entry.
309*/
310
311/*!
312 \internal
313 For compatibility, non-const version.
314*/
315QString Config::readEntry( const QString &key, const QString &deflt )
316{
317 QString res = readEntryDirect( key+"["+lang+"]" );
318 if ( !res.isNull() )
319 return res;
320 if ( !glang.isEmpty() ) {
321 res = readEntryDirect( key+"["+glang+"]" );
322 if ( !res.isNull() )
323 return res;
324 }
325 return readEntryDirect( key, deflt );
326}
327
328/*!
329 \fn QString Config::readEntryCrypt( const QString &key, const QString &deflt ) const
330
331 Reads an encrypted string entry stored with \a key, defaulting to \a deflt if there is no entry.
332*/
333
334/*!
335 \internal
336 For compatibility, non-const version.
337*/
338QString Config::readEntryCrypt( const QString &key, const QString &deflt )
339{
340 QString res = readEntryDirect( key+"["+lang+"]" );
341 if ( res.isNull() && glang.isEmpty() )
342 res = readEntryDirect( key+"["+glang+"]" );
343 if ( res.isNull() )
344 res = readEntryDirect( key, QString::null );
345 if ( res.isNull() )
346 return deflt;
347 return decipher(res);
348}
349
350/*!
351 \fn QString Config::readEntryDirect( const QString &key, const QString &deflt ) const
352 \internal
353*/
354
355/*!
356 \internal
357 For compatibility, non-const version.
358*/
359QString Config::readEntryDirect( const QString &key, const QString &deflt )
360{
361 if ( git == groups.end() ) {
362 //qWarning( "no group set" );
363 return deflt;
364 }
365 ConfigGroup::ConstIterator it = ( *git ).find( key );
366 if ( it != ( *git ).end() )
367 return *it;
368 else
369 return deflt;
370}
371
372/*!
373 \fn int Config::readNumEntry( const QString &key, int deflt ) const
374 Reads a numeric entry stored with \a key, defaulting to \a deflt if there is no entry.
375*/
376
377/*!
378 \internal
379 For compatibility, non-const version.
380*/
381int Config::readNumEntry( const QString &key, int deflt )
382{
383 QString s = readEntry( key );
384 if ( s.isEmpty() )
385 return deflt;
386 else
387 return s.toInt();
388}
389
390/*!
391 \fn bool Config::readBoolEntry( const QString &key, bool deflt ) const
392 Reads a bool entry stored with \a key, defaulting to \a deflt if there is no entry.
393*/
394
395/*!
396 \internal
397 For compatibility, non-const version.
398*/
399bool Config::readBoolEntry( const QString &key, bool deflt )
400{
401 QString s = readEntry( key );
402 if ( s.isEmpty() )
403 return deflt;
404 else
405 return (bool)s.toInt();
406}
407
408/*!
409 \fn QStringList Config::readListEntry( const QString &key, const QChar &sep ) const
410 Reads a string list entry stored with \a key, and with \a sep as the separator.
411*/
412
413/*!
414 \internal
415 For compatibility, non-const version.
416*/
417QStringList Config::readListEntry( const QString &key, const QChar &sep )
418{
419 QString s = readEntry( key );
420 if ( s.isEmpty() )
421 return QStringList();
422 else
423 return QStringList::split( sep, s );
424}
425
426/*!
427 Removes all entries from the current group.
428*/
429void Config::clearGroup()
430{
431 if ( git == groups.end() ) {
432 qWarning( "no group set" );
433 return;
434 }
435 if ( !(*git).isEmpty() ) {
436 ( *git ).clear();
437 changed = TRUE;
438 }
439}
440
441/*!
442 \internal
443*/
444void Config::write( const QString &fn )
445{
446 QString strNewFile;
447 if ( !fn.isEmpty() )
448 filename = fn;
449 strNewFile = filename + ".new";
450
451 QFile f( strNewFile );
452 if ( !f.open( IO_WriteOnly|IO_Raw ) ) {
453 qWarning( "could not open for writing `%s'", strNewFile.latin1() );
454 git = groups.end();
455 return;
456 }
457
458 QString str;
459 QCString cstr;
460 QMap< QString, ConfigGroup >::Iterator g_it = groups.begin();
461
462 for ( ; g_it != groups.end(); ++g_it ) {
463 str += "[" + g_it.key() + "]\n";
464 ConfigGroup::Iterator e_it = ( *g_it ).begin();
465 for ( ; e_it != ( *g_it ).end(); ++e_it )
466 str += e_it.key() + " = " + *e_it + "\n";
467 }
468 cstr = str.utf8();
469
470 int total_length;
471 total_length = f.writeBlock( cstr.data(), cstr.length() );
472 if ( total_length != int(cstr.length()) ) {
473 QMessageBox::critical( 0, QObject::tr("Out of Space"),
474 QObject::tr("There was a problem creating\nConfiguration Information \nfor this program.\n\nPlease free up some space and\ntry again.") );
475 f.close();
476 QFile::remove( strNewFile );
477 return;
478 }
479
480 f.close();
481 // now rename the file...
482 if ( rename( strNewFile, filename ) < 0 ) {
483 qWarning( "problem renaming the file %s to %s", strNewFile.latin1(),
484 filename.latin1() );
485 QFile::remove( strNewFile );
486 }
487}
488
489/*!
490 Returns whether the Config is in a valid state.
491*/
492bool Config::isValid() const
493{
494 return groups.end() != git;
495}
496
497/*!
498 \internal
499*/
500void Config::read()
501{
502 changed = FALSE;
503
504 if ( !QFileInfo( filename ).exists() ) {
505 git = groups.end();
506 return;
507 }
508
509 QFile f( filename );
510 if ( !f.open( IO_ReadOnly ) ) {
511 git = groups.end();
512 return;
513 }
514
515 QTextStream s( &f );
516#if QT_VERSION <= 230 && defined(QT_NO_CODECS)
517 // The below should work, but doesn't in Qt 2.3.0
518 s.setCodec( QTextCodec::codecForMib( 106 ) );
519#else
520 s.setEncoding( QTextStream::UnicodeUTF8 );
521#endif
522
523 QStringList list = QStringList::split('\n', s.read() );
524 f.close();
525
526 for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
527 if ( !parse( *it ) ) {
528 git = groups.end();
529 return;
530 }
531 }
532}
533
534/*!
535 \internal
536*/
537bool Config::parse( const QString &l )
538{
539 QString line = l.stripWhiteSpace();
540 if ( line[ 0 ] == QChar( '[' ) ) {
541 QString gname = line;
542 gname = gname.remove( 0, 1 );
543 if ( gname[ (int)gname.length() - 1 ] == QChar( ']' ) )
544 gname = gname.remove( gname.length() - 1, 1 );
545 git = groups.insert( gname, ConfigGroup() );
546 } else if ( !line.isEmpty() ) {
547 if ( git == groups.end() )
548 return FALSE;
549 int eq = line.find( '=' );
550 if ( eq == -1 )
551 return FALSE;
552 QString key = line.left(eq).stripWhiteSpace();
553 QString value = line.mid(eq+1).stripWhiteSpace();
554 ( *git ).insert( key, value );
555 }
556 return TRUE;
557}