fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kconf_update / kconf_update.cpp
blob0e6568b3c06850395aa4644e767c16072d2e536e
1 /*
3 * This file is part of the KDE libraries
4 * Copyright (c) 2001 Waldo Bastian <bastian@kde.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License version 2 as published by the Free Software Foundation.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 **/
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <kde_file.h>
27 #include <QtCore/QDate>
28 #include <QtCore/QFile>
29 #include <QtCore/QTextStream>
30 #include <QtCore/QTextCodec>
32 #include <kconfig.h>
33 #include <kconfiggroup.h>
34 #include <klocale.h>
35 #include <kcmdlineargs.h>
36 #include <kglobal.h>
37 #include <kstandarddirs.h>
38 #include <kaboutdata.h>
39 #include <kcomponentdata.h>
40 #include <ktemporaryfile.h>
41 #include <kurl.h>
43 class KonfUpdate
45 public:
46 KonfUpdate();
47 ~KonfUpdate();
48 QStringList findUpdateFiles(bool dirtyOnly);
50 QTextStream &log();
52 bool checkFile(const QString &filename);
53 void checkGotFile(const QString &_file, const QString &id);
55 bool updateFile(const QString &filename);
57 void gotId(const QString &_id);
58 void gotFile(const QString &_file);
59 void gotGroup(const QString &_group);
60 void gotRemoveGroup(const QString &_group);
61 void gotKey(const QString &_key);
62 void gotRemoveKey(const QString &_key);
63 void gotAllKeys();
64 void gotAllGroups();
65 void gotOptions(const QString &_options);
66 void gotScript(const QString &_script);
67 void gotScriptArguments(const QString &_arguments);
68 void resetOptions();
70 void copyGroup(KConfig *cfg1, const QString &group1,
71 KConfig *cfg2, const QString &group2);
73 protected:
74 KConfig *config;
75 QString currentFilename;
76 bool skip;
77 bool skipFile;
78 bool debug;
79 QString id;
81 QString oldFile;
82 QString newFile;
83 QString newFileName;
84 KConfig *oldConfig1; // Config to read keys from.
85 KConfig *oldConfig2; // Config to delete keys from.
86 KConfig *newConfig;
88 QString oldGroup;
89 QString newGroup;
90 QString oldKey;
91 QString newKey;
93 bool m_bCopy;
94 bool m_bOverwrite;
95 bool m_bUseConfigInfo;
96 QString m_arguments;
97 QTextStream *m_textStream;
98 QFile *m_file;
99 QString m_line;
100 int m_lineCount;
103 KonfUpdate::KonfUpdate()
104 : m_textStream(0), m_file(0)
106 bool updateAll = false;
107 oldConfig1 = 0;
108 oldConfig2 = 0;
109 newConfig = 0;
111 config = new KConfig("kconf_updaterc");
112 KConfigGroup cg(config, QString());
114 QStringList updateFiles;
115 KCmdLineArgs *args=KCmdLineArgs::parsedArgs();
117 debug = args->isSet("debug");
119 m_bUseConfigInfo = false;
120 if (args->isSet("check"))
122 m_bUseConfigInfo = true;
123 QString file = KStandardDirs::locate("data", "kconf_update/"+args->getOption("check"));
124 if (file.isEmpty())
126 qWarning("File '%s' not found.", args->getOption("check").toLocal8Bit().data());
127 log() << "File '" << args->getOption("check") << "' passed on command line not found" << endl;
128 return;
130 updateFiles.append(file);
132 else if (args->count())
134 for(int i = 0; i < args->count(); i++)
136 KUrl url = args->url(i);
137 if (!url.isLocalFile())
138 KCmdLineArgs::usageError(i18n("Only local files are supported."));
139 updateFiles.append(url.path());
142 else
144 if (cg.readEntry("autoUpdateDisabled", false))
145 return;
146 updateFiles = findUpdateFiles(true);
147 updateAll = true;
150 for(QStringList::ConstIterator it = updateFiles.constBegin();
151 it != updateFiles.constEnd();
152 ++it)
154 updateFile(*it);
157 if (updateAll && !cg.readEntry("updateInfoAdded", false))
159 cg.writeEntry("updateInfoAdded", true);
160 updateFiles = findUpdateFiles(false);
162 for(QStringList::ConstIterator it = updateFiles.constBegin();
163 it != updateFiles.constEnd();
164 ++it)
166 checkFile(*it);
168 updateFiles.clear();
172 KonfUpdate::~KonfUpdate()
174 delete config;
175 delete m_file;
176 delete m_textStream;
179 QTextStream &
180 KonfUpdate::log()
182 if (!m_textStream)
184 QString file = KStandardDirs::locateLocal("data", "kconf_update/log/update.log");
185 m_file = new QFile(file);
186 if (m_file->open(QIODevice::WriteOnly | QIODevice::Append))
188 m_textStream = new QTextStream(m_file);
190 else
192 // Error
193 m_textStream = new QTextStream(stderr, QIODevice::WriteOnly);
197 (*m_textStream) << QDateTime::currentDateTime().toString( Qt::ISODate ) << " ";
199 return *m_textStream;
202 QStringList KonfUpdate::findUpdateFiles(bool dirtyOnly)
204 QStringList result;
205 const QStringList list = KGlobal::dirs()->findAllResources("data", "kconf_update/*.upd",
206 KStandardDirs::NoDuplicates);
207 for(QStringList::ConstIterator it = list.constBegin();
208 it != list.constEnd();
209 ++it)
211 QString file = *it;
212 KDE_struct_stat buff;
213 if (KDE::stat(file, &buff) == 0)
215 int i = file.lastIndexOf('/');
216 if (i != -1)
217 file = file.mid(i+1);
218 KConfigGroup cg(config, file);
219 time_t ctime = cg.readEntry("ctime", 0);
220 time_t mtime = cg.readEntry("mtime", 0);
221 if (!dirtyOnly ||
222 (ctime != buff.st_ctime) || (mtime != buff.st_mtime))
224 result.append(*it);
228 return result;
231 bool KonfUpdate::checkFile(const QString &filename)
233 currentFilename = filename;
234 int i = currentFilename.lastIndexOf('/');
235 if (i != -1)
236 currentFilename = currentFilename.mid(i+1);
237 skip = true;
238 QFile file(filename);
239 if (!file.open(QIODevice::ReadOnly))
240 return false;
242 QTextStream ts(&file);
243 ts.setCodec(QTextCodec::codecForName("ISO-8859-1"));
244 int lineCount = 0;
245 resetOptions();
246 QString id;
247 while(!ts.atEnd())
249 QString line = ts.readLine().trimmed();
250 lineCount++;
251 if (line.isEmpty() || (line[0] == '#'))
252 continue;
253 if (line.startsWith("Id="))
254 id = currentFilename+':'+line.mid(3);
255 else if (line.startsWith("File="))
256 checkGotFile(line.mid(5), id);
259 return true;
262 void KonfUpdate::checkGotFile(const QString &_file, const QString &id)
264 QString file;
265 int i = _file.indexOf(',');
266 if (i == -1)
268 file = _file.trimmed();
270 else
272 file = _file.mid(i+1).trimmed();
275 // qDebug("File %s, id %s", file.toLatin1().constData(), id.toLatin1().constData());
277 KConfig cfg(file, KConfig::SimpleConfig);
278 KConfigGroup cg(&cfg, "$Version");
279 QStringList ids = cg.readEntry("update_info", QStringList());
280 if (ids.contains(id))
281 return;
282 ids.append(id);
283 cg.writeEntry("update_info", ids);
287 * Syntax:
288 * # Comment
289 * Id=id
290 * File=oldfile[,newfile]
291 * AllGroups
292 * Group=oldgroup[,newgroup]
293 * RemoveGroup=oldgroup
294 * Options=[copy,][overwrite,]
295 * Key=oldkey[,newkey]
296 * RemoveKey=ldkey
297 * AllKeys
298 * Keys= [Options](AllKeys|(Key|RemoveKey)*)
299 * ScriptArguments=arguments
300 * Script=scriptfile[,interpreter]
302 * Sequence:
303 * (Id,(File(Group,Keys)*)*)*
305 bool KonfUpdate::updateFile(const QString &filename)
307 currentFilename = filename;
308 int i = currentFilename.lastIndexOf('/');
309 if (i != -1)
310 currentFilename = currentFilename.mid(i+1);
311 skip = true;
312 QFile file(filename);
313 if (!file.open(QIODevice::ReadOnly))
314 return false;
316 log() << "Checking update-file '" << filename << "' for new updates" << endl;
318 QTextStream ts(&file);
319 ts.setCodec(QTextCodec::codecForName("ISO-8859-1"));
320 m_lineCount = 0;
321 resetOptions();
322 while(!ts.atEnd())
324 m_line = ts.readLine().trimmed();
325 m_lineCount++;
326 if (m_line.isEmpty() || (m_line[0] == '#'))
327 continue;
328 if (m_line.startsWith("Id="))
329 gotId(m_line.mid(3));
330 else if (skip)
331 continue;
332 else if (m_line.startsWith("Options="))
333 gotOptions(m_line.mid(8));
334 else if (m_line.startsWith("File="))
335 gotFile(m_line.mid(5));
336 else if(skipFile)
337 continue;
338 else if (m_line.startsWith("Group="))
339 gotGroup(m_line.mid(6));
340 else if (m_line.startsWith("RemoveGroup="))
342 gotRemoveGroup(m_line.mid(12));
343 resetOptions();
345 else if (m_line.startsWith("Script="))
347 gotScript(m_line.mid(7));
348 resetOptions();
350 else if (m_line.startsWith("ScriptArguments="))
351 gotScriptArguments(m_line.mid(16));
352 else if (m_line.startsWith("Key="))
354 gotKey(m_line.mid(4));
355 resetOptions();
357 else if (m_line.startsWith("RemoveKey="))
359 gotRemoveKey(m_line.mid(10));
360 resetOptions();
362 else if (m_line == "AllKeys")
364 gotAllKeys();
365 resetOptions();
367 else if (m_line == "AllGroups")
369 gotAllGroups();
370 resetOptions();
372 else
374 log() << currentFilename << ": parse error in line " << m_lineCount << " : '" << m_line << "'" << endl;
377 // Flush.
378 gotId(QString());
380 KDE_struct_stat buff;
381 KDE::stat(filename, &buff);
382 KConfigGroup cg(config, currentFilename);
383 cg.writeEntry("ctime", int(buff.st_ctime));
384 cg.writeEntry("mtime", int(buff.st_mtime));
385 cg.sync();
386 return true;
391 void KonfUpdate::gotId(const QString &_id)
393 if (!id.isEmpty() && !skip)
395 KConfigGroup cg(config, currentFilename);
397 QStringList ids = cg.readEntry("done", QStringList());
398 if (!ids.contains(id))
400 ids.append(id);
401 cg.writeEntry("done", ids);
402 cg.sync();
406 // Flush pending changes
407 gotFile(QString());
408 KConfigGroup cg(config, currentFilename);
410 QStringList ids = cg.readEntry("done", QStringList());
411 if (!_id.isEmpty())
413 if (ids.contains(_id))
415 //qDebug("Id '%s' was already in done-list", _id.toLatin1().constData());
416 if (!m_bUseConfigInfo)
418 skip = true;
419 return;
422 skip = false;
423 skipFile = false;
424 id = _id;
425 if (m_bUseConfigInfo)
426 log() << currentFilename << ": Checking update '" << _id << "'" << endl;
427 else
428 log() << currentFilename << ": Found new update '" << _id << "'" << endl;
432 void KonfUpdate::gotFile(const QString &_file)
434 // Reset group
435 gotGroup(QString());
437 if (!oldFile.isEmpty())
439 // Close old file.
440 delete oldConfig1;
441 oldConfig1 = 0;
443 KConfigGroup cg(oldConfig2, "$Version");
444 QStringList ids = cg.readEntry("update_info", QStringList());
445 QString cfg_id = currentFilename + ':' + id;
446 if (!ids.contains(cfg_id) && !skip)
448 ids.append(cfg_id);
449 cg.writeEntry("update_info", ids);
451 cg.sync();
452 delete oldConfig2;
453 oldConfig2 = 0;
455 QString file = KStandardDirs::locateLocal("config", oldFile);
456 KDE_struct_stat s_buf;
457 if (KDE::stat(file, &s_buf) == 0)
459 if (s_buf.st_size == 0)
461 // Delete empty file.
462 QFile::remove(file);
466 oldFile.clear();
468 if (!newFile.isEmpty())
470 // Close new file.
471 KConfigGroup cg(newConfig, "$Version");
472 QStringList ids = cg.readEntry("update_info", QStringList());
473 QString cfg_id = currentFilename + ':' + id;
474 if (!ids.contains(cfg_id) && !skip)
476 ids.append(cfg_id);
477 cg.writeEntry("update_info", ids);
479 newConfig->sync();
480 delete newConfig;
481 newConfig = 0;
483 newFile.clear();
485 newConfig = 0;
487 int i = _file.indexOf(',');
488 if (i == -1)
490 oldFile = _file.trimmed();
492 else
494 oldFile = _file.left(i).trimmed();
495 newFile = _file.mid(i+1).trimmed();
496 if (oldFile == newFile)
497 newFile.clear();
500 if (!oldFile.isEmpty())
502 oldConfig2 = new KConfig(oldFile, KConfig::NoGlobals);
503 QString cfg_id = currentFilename + ':' + id;
504 KConfigGroup cg(oldConfig2, "$Version");
505 QStringList ids = cg.readEntry("update_info", QStringList());
506 if (ids.contains(cfg_id))
508 skip = true;
509 newFile.clear();
510 log() << currentFilename << ": Skipping update '" << id << "'" << endl;
513 if (!newFile.isEmpty())
515 newConfig = new KConfig(newFile, KConfig::NoGlobals);
516 KConfigGroup cg(newConfig, "$Version");
517 ids = cg.readEntry("update_info", QStringList());
518 if (ids.contains(cfg_id))
520 skip = true;
521 log() << currentFilename << ": Skipping update '" << id << "'" << endl;
524 else
526 newConfig = oldConfig2;
529 oldConfig1 = new KConfig(oldFile, KConfig::NoGlobals);
531 else
533 newFile.clear();
535 newFileName = newFile;
536 if (newFileName.isEmpty())
537 newFileName = oldFile;
539 skipFile = false;
540 if( !oldFile.isEmpty())
541 { // if File= is specified, it doesn't exist, is empty or contains only kconf_update's [$Version] group, skip
542 if( oldConfig1 != NULL
543 && ( oldConfig1->groupList().isEmpty()
544 || ( oldConfig1->groupList().count() == 1 && oldConfig1->groupList().first() == "$Version" )))
546 log() << currentFilename << ": File '" << oldFile << "' does not exist or empty, skipping" << endl;
547 skipFile = true;
552 void KonfUpdate::gotGroup(const QString &_group)
554 int i = _group.indexOf(',');
555 if (i == -1)
557 oldGroup = _group.trimmed();
558 newGroup = oldGroup;
560 else
562 oldGroup = _group.left(i).trimmed();
563 newGroup = _group.mid(i+1).trimmed();
567 void KonfUpdate::gotRemoveGroup(const QString &_group)
569 oldGroup = _group.trimmed();
571 if (!oldConfig1)
573 log() << currentFilename << ": !! RemoveGroup without previous File specification in line " << m_lineCount << " : '" << m_line << "'" << endl;
574 return;
577 if (!oldConfig1->hasGroup(oldGroup))
578 return;
579 // Delete group.
580 oldConfig2->deleteGroup(oldGroup);
581 log() << currentFilename << ": RemoveGroup removes group " << oldFile << ":" << oldGroup << endl;
585 void KonfUpdate::gotKey(const QString &_key)
587 int i = _key.indexOf(',');
588 if (i == -1)
590 oldKey = _key.trimmed();
591 newKey = oldKey;
593 else
595 oldKey = _key.left(i).trimmed();
596 newKey = _key.mid(i+1).trimmed();
599 if (oldKey.isEmpty() || newKey.isEmpty())
601 log() << currentFilename << ": !! Key specifies invalid key in line " << m_lineCount << " : '" << m_line << "'" << endl;
602 return;
604 if (!oldConfig1)
606 log() << currentFilename << ": !! Key without previous File specification in line " << m_lineCount << " : '" << m_line << "'" << endl;
607 return;
609 KConfigGroup cg1( oldConfig1, oldGroup);
610 if (!cg1.hasKey(oldKey))
611 return;
612 QString value = cg1.readEntry(oldKey, QString());
613 KConfigGroup newFGroup( newConfig, newGroup);
614 if (!m_bOverwrite && newFGroup.hasKey(newKey))
616 log() << currentFilename << ": Skipping " << newFileName << ":" << newGroup << ":" << newKey << ", already exists."<< endl;
617 return;
619 log() << currentFilename << ": Updating " << newFileName << ":" << newGroup << ":" << newKey << " to '" << value << "'" << endl;
620 newFGroup.writeEntry(newKey, value);
622 if (m_bCopy)
623 return; // Done.
625 // Delete old entry
626 if ((oldConfig2 == newConfig) &&
627 (oldGroup == newGroup) &&
628 (oldKey == newKey))
629 return; // Don't delete!
630 KConfigGroup oldGroup2( oldConfig2, oldGroup);
631 oldGroup2.deleteEntry(oldKey);
632 log() << currentFilename << ": Removing " << oldFile << ":" << oldGroup << ":" << oldKey << ", moved." << endl;
633 /*if (oldConfig2->deleteGroup(oldGroup, KConfig::Normal)) { // Delete group if empty.
634 log() << currentFilename << ": Removing empty group " << oldFile << ":" << oldGroup << endl;
635 } (this should be automatic) */
638 void KonfUpdate::gotRemoveKey(const QString &_key)
640 oldKey = _key.trimmed();
642 if (oldKey.isEmpty())
644 log() << currentFilename << ": !! RemoveKey specifies invalid key in line " << m_lineCount << " : '" << m_line << "'" << endl;
645 return;
648 if (!oldConfig1)
650 log() << currentFilename << ": !! Key without previous File specification in line " << m_lineCount << " : '" << m_line << "'" << endl;
651 return;
654 KConfigGroup cg1(oldConfig1, oldGroup);
655 if (!cg1.hasKey(oldKey))
656 return;
657 log() << currentFilename << ": RemoveKey removes " << oldFile << ":" << oldGroup << ":" << oldKey << endl;
659 // Delete old entry
660 KConfigGroup cg2( oldConfig2, oldGroup);
661 cg2.deleteEntry(oldKey);
662 /*if (oldConfig2->deleteGroup(oldGroup, KConfig::Normal)) { // Delete group if empty.
663 log() << currentFilename << ": Removing empty group " << oldFile << ":" << oldGroup << endl;
664 } (this should be automatic)*/
667 void KonfUpdate::gotAllKeys()
669 if (!oldConfig1)
671 log() << currentFilename << ": !! AllKeys without previous File specification in line " << m_lineCount << " : '" << m_line << "'" << endl;
672 return;
675 QMap<QString, QString> list = oldConfig1->entryMap(oldGroup);
676 for(QMap<QString, QString>::Iterator it = list.begin();
677 it != list.end(); ++it)
679 gotKey(it.key());
683 void KonfUpdate::gotAllGroups()
685 if (!oldConfig1)
687 log() << currentFilename << ": !! AllGroups without previous File specification in line " << m_lineCount << " : '" << m_line << "'" << endl;
688 return;
691 const QStringList allGroups = oldConfig1->groupList();
692 for(QStringList::ConstIterator it = allGroups.begin();
693 it != allGroups.end(); ++it)
695 oldGroup = *it;
696 newGroup = oldGroup;
697 gotAllKeys();
701 void KonfUpdate::gotOptions(const QString &_options)
703 const QStringList options = _options.split(',');
704 for(QStringList::ConstIterator it = options.begin();
705 it != options.end();
706 ++it)
708 if ( (*it).toLower().trimmed() == "copy")
709 m_bCopy = true;
711 if ( (*it).toLower().trimmed() == "overwrite")
712 m_bOverwrite = true;
716 void KonfUpdate::copyGroup(KConfig *cfg1, const QString &group1,
717 KConfig *cfg2, const QString &group2)
719 KConfigGroup cg1(cfg1, group1);
720 KConfigGroup cg2(cfg2, group2);
721 QMap<QString, QString> list = cg1.entryMap();
722 for(QMap<QString, QString>::Iterator it = list.begin();
723 it != list.end(); ++it)
725 cg2.writeEntry(it.key(), cg1.readEntry(it.key(), QString()));
729 void KonfUpdate::gotScriptArguments(const QString &_arguments)
731 m_arguments = _arguments;
734 void KonfUpdate::gotScript(const QString &_script)
736 QString script, interpreter;
737 int i = _script.indexOf(',');
738 if (i == -1)
740 script = _script.trimmed();
742 else
744 script = _script.left(i).trimmed();
745 interpreter = _script.mid(i+1).trimmed();
749 if (script.isEmpty())
751 log() << currentFilename << ": !! Script fails to specify filename in line " << m_lineCount << " : '" << m_line << "'" << endl;
752 skip = true;
753 return;
758 QString path = KStandardDirs::locate("data","kconf_update/"+script);
759 if (path.isEmpty())
761 if (interpreter.isEmpty())
762 path = KStandardDirs::locate("lib", "kconf_update_bin/"+script);
764 if (path.isEmpty())
766 log() << currentFilename << ": !! Script '" << script << "' not found in line " << m_lineCount << " : '" << m_line << "'" << endl;
767 skip = true;
768 return;
772 if( !m_arguments.isNull())
773 log() << currentFilename << ": Running script '" << script << "' with arguments '" << m_arguments << "'" << endl;
774 else
775 log() << currentFilename << ": Running script '" << script << "'" << endl;
777 QString cmd;
778 if (interpreter.isEmpty())
779 cmd = path;
780 else
781 cmd = interpreter + ' ' + path;
783 if( !m_arguments.isNull())
785 cmd += ' ';
786 cmd += m_arguments;
789 KTemporaryFile tmp1;
790 tmp1.open();
791 KTemporaryFile tmp2;
792 tmp2.open();
793 KTemporaryFile tmp3;
794 tmp3.open();
796 int result;
797 if (oldConfig1)
799 if (debug)
801 tmp1.setAutoRemove(false);
802 log() << "Script input stored in " << tmp1.fileName() << endl;
804 KConfig cfg(tmp1.fileName(), KConfig::SimpleConfig);
806 if (oldGroup.isEmpty())
808 // Write all entries to tmpFile;
809 const QStringList grpList = oldConfig1->groupList();
810 for(QStringList::ConstIterator it = grpList.begin();
811 it != grpList.end();
812 ++it)
814 copyGroup(oldConfig1, *it, &cfg, *it);
817 else
819 copyGroup(oldConfig1, oldGroup, &cfg, QString());
821 cfg.sync();
822 result = system(QFile::encodeName(QString("%1 < %2 > %3 2> %4").arg(cmd, tmp1.fileName(), tmp2.fileName(), tmp3.fileName())));
824 else
826 // No config file
827 result = system(QFile::encodeName(QString("%1 2> %2").arg(cmd, tmp3.fileName())));
830 // Copy script stderr to log file
832 QFile output(tmp3.fileName());
833 if (output.open(QIODevice::ReadOnly))
835 QTextStream ts( &output );
836 ts.setCodec(QTextCodec::codecForName("UTF-8"));
837 while(!ts.atEnd())
839 QString line = ts.readLine();
840 log() << "[Script] " << line << endl;
845 if (result)
847 log() << currentFilename << ": !! An error occurred while running '" << cmd << "'" << endl;
848 return;
851 if (!oldConfig1)
852 return; // Nothing to merge
854 if (debug)
856 tmp2.setAutoRemove(false);
857 log() << "Script output stored in " << tmp2.fileName() << endl;
860 // Deleting old entries
862 QString group = oldGroup;
863 QFile output(tmp2.fileName());
864 if (output.open(QIODevice::ReadOnly))
866 QTextStream ts( &output );
867 ts.setCodec(QTextCodec::codecForName("UTF-8"));
868 while(!ts.atEnd())
870 QString line = ts.readLine();
871 if (line.startsWith('['))
873 int j = line.indexOf(']')+1;
874 if (j > 0)
875 group = line.mid(1, j-2);
877 else if (line.startsWith("# DELETE "))
879 QString key = line.mid(9);
880 if (key[0] == '[')
882 int j = key.indexOf(']')+1;
883 if (j > 0)
885 group = key.mid(1,j-2);
886 key = key.mid(j);
889 KConfigGroup cg(oldConfig2, group);
890 cg.deleteEntry(key);
891 log() << currentFilename << ": Script removes " << oldFile << ":" << group << ":" << key << endl;
892 /*if (oldConfig2->deleteGroup(group, KConfig::Normal)) { // Delete group if empty.
893 log() << currentFilename << ": Removing empty group " << oldFile << ":" << group << endl;
894 } (this should be automatic)*/
896 else if (line.startsWith("# DELETEGROUP"))
898 QString key = line.mid(13).trimmed();
899 if (key[0] == '[')
901 int j = key.indexOf(']')+1;
902 if (j > 0)
904 group = key.mid(1,j-2);
907 oldConfig2->deleteGroup(group);
908 log() << currentFilename << ": Script removes group " << oldFile << ":" << group << endl;
914 // Merging in new entries.
915 m_bCopy = true;
917 KConfig *saveOldConfig1 = oldConfig1;
918 QString saveOldGroup = oldGroup;
919 QString saveNewGroup = newGroup;
920 oldConfig1 = new KConfig(tmp2.fileName(), KConfig::NoGlobals);
922 // For all groups...
923 const QStringList grpList = oldConfig1->groupList();
924 for(QStringList::ConstIterator it = grpList.begin();
925 it != grpList.end();
926 ++it)
928 oldGroup = *it;
929 if (oldGroup != "<default>")
931 newGroup = oldGroup;
933 gotAllKeys(); // Copy all keys
935 delete oldConfig1;
936 oldConfig1 = saveOldConfig1;
937 oldGroup = saveOldGroup;
938 newGroup = saveNewGroup;
942 void KonfUpdate::resetOptions()
944 m_bCopy = false;
945 m_bOverwrite = false;
946 m_arguments.clear();
950 extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
952 KCmdLineOptions options;
953 options.add("debug", ki18n("Keep output results from scripts"));
954 options.add("check <update-file>", ki18n("Check whether config file itself requires updating"));
955 options.add("+[file]", ki18n("File to read update instructions from"));
957 KAboutData aboutData("kconf_update", 0, ki18n("KConf Update"),
958 "1.0.2",
959 ki18n("KDE Tool for updating user configuration files"),
960 KAboutData::License_GPL,
961 ki18n("(c) 2001, Waldo Bastian"));
963 aboutData.addAuthor(ki18n("Waldo Bastian"), KLocalizedString(), "bastian@kde.org");
965 KCmdLineArgs::init(argc, argv, &aboutData);
966 KCmdLineArgs::addCmdLineOptions(options);
968 KComponentData componentData(&aboutData);
970 KonfUpdate konfUpdate;
972 return 0;