kconf_update
kconf_update.cpp
Go to the documentation of this file.
00001 /* 00002 * 00003 * This file is part of the KDE libraries 00004 * Copyright (c) 2001 Waldo Bastian <bastian@kde.org> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License version 2 as published by the Free Software Foundation. 00009 * 00010 * This library is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 * Library General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU Library General Public License 00016 * along with this library; see the file COPYING.LIB. If not, write to 00017 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 * Boston, MA 02110-1301, USA. 00019 **/ 00020 00021 #include <sys/types.h> 00022 #include <sys/stat.h> 00023 #include <unistd.h> 00024 #include <stdlib.h> 00025 #include <kde_file.h> 00026 00027 #include <QtCore/QDate> 00028 #include <QtCore/QFile> 00029 #include <QtCore/QTextStream> 00030 #include <QtCore/QTextCodec> 00031 #ifdef _WIN32_WCE 00032 #include <QtCore/QDir> 00033 #endif 00034 00035 #include <kconfig.h> 00036 #include <kconfiggroup.h> 00037 #include <kdebug.h> 00038 #include <klocale.h> 00039 #include <kcmdlineargs.h> 00040 #include <kglobal.h> 00041 #include <kstandarddirs.h> 00042 #include <kaboutdata.h> 00043 #include <kcomponentdata.h> 00044 #include <ktemporaryfile.h> 00045 #include <kurl.h> 00046 00047 #include "kconfigutils.h" 00048 00049 class KonfUpdate 00050 { 00051 public: 00052 KonfUpdate(); 00053 ~KonfUpdate(); 00054 QStringList findUpdateFiles(bool dirtyOnly); 00055 00056 QTextStream &log(); 00057 QTextStream &logFileError(); 00058 00059 bool checkFile(const QString &filename); 00060 void checkGotFile(const QString &_file, const QString &id); 00061 00062 bool updateFile(const QString &filename); 00063 00064 void gotId(const QString &_id); 00065 void gotFile(const QString &_file); 00066 void gotGroup(const QString &_group); 00067 void gotRemoveGroup(const QString &_group); 00068 void gotKey(const QString &_key); 00069 void gotRemoveKey(const QString &_key); 00070 void gotAllKeys(); 00071 void gotAllGroups(); 00072 void gotOptions(const QString &_options); 00073 void gotScript(const QString &_script); 00074 void gotScriptArguments(const QString &_arguments); 00075 void resetOptions(); 00076 00077 void copyGroup(const KConfigBase *cfg1, const QString &group1, 00078 KConfigBase *cfg2, const QString &group2); 00079 void copyGroup(const KConfigGroup &cg1, KConfigGroup &cg2); 00080 void copyOrMoveKey(const QStringList &srcGroupPath, const QString &srcKey, const QStringList &dstGroupPath, const QString &dstKey); 00081 void copyOrMoveGroup(const QStringList &srcGroupPath, const QStringList &dstGroupPath); 00082 00083 QStringList parseGroupString(const QString &_str); 00084 00085 protected: 00086 KConfig *m_config; 00087 QString m_currentFilename; 00088 bool m_skip; 00089 bool m_skipFile; 00090 bool m_debug; 00091 QString m_id; 00092 00093 QString m_oldFile; 00094 QString m_newFile; 00095 QString m_newFileName; 00096 KConfig *m_oldConfig1; // Config to read keys from. 00097 KConfig *m_oldConfig2; // Config to delete keys from. 00098 KConfig *m_newConfig; 00099 00100 QStringList m_oldGroup; 00101 QStringList m_newGroup; 00102 00103 bool m_bCopy; 00104 bool m_bOverwrite; 00105 bool m_bUseConfigInfo; 00106 QString m_arguments; 00107 QTextStream *m_textStream; 00108 QFile *m_file; 00109 QString m_line; 00110 int m_lineCount; 00111 }; 00112 00113 KonfUpdate::KonfUpdate() 00114 : m_textStream(0), m_file(0) 00115 { 00116 bool updateAll = false; 00117 m_oldConfig1 = 0; 00118 m_oldConfig2 = 0; 00119 m_newConfig = 0; 00120 00121 m_config = new KConfig("kconf_updaterc"); 00122 KConfigGroup cg(m_config, QString()); 00123 00124 QStringList updateFiles; 00125 KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); 00126 00127 m_debug = args->isSet("debug"); 00128 00129 m_bUseConfigInfo = false; 00130 if (args->isSet("check")) { 00131 m_bUseConfigInfo = true; 00132 QString file = KStandardDirs::locate("data", "kconf_update/" + args->getOption("check")); 00133 if (file.isEmpty()) { 00134 qWarning("File '%s' not found.", args->getOption("check").toLocal8Bit().data()); 00135 log() << "File '" << args->getOption("check") << "' passed on command line not found" << endl; 00136 return; 00137 } 00138 updateFiles.append(file); 00139 } else if (args->count()) { 00140 for (int i = 0; i < args->count(); i++) { 00141 KUrl url = args->url(i); 00142 if (!url.isLocalFile()) { 00143 KCmdLineArgs::usageError(i18n("Only local files are supported.")); 00144 } 00145 updateFiles.append(url.path()); 00146 } 00147 } else { 00148 if (cg.readEntry("autoUpdateDisabled", false)) 00149 return; 00150 updateFiles = findUpdateFiles(true); 00151 updateAll = true; 00152 } 00153 00154 for (QStringList::ConstIterator it = updateFiles.constBegin(); 00155 it != updateFiles.constEnd(); 00156 ++it) { 00157 updateFile(*it); 00158 } 00159 00160 if (updateAll && !cg.readEntry("updateInfoAdded", false)) { 00161 cg.writeEntry("updateInfoAdded", true); 00162 updateFiles = findUpdateFiles(false); 00163 00164 for (QStringList::ConstIterator it = updateFiles.constBegin(); 00165 it != updateFiles.constEnd(); 00166 ++it) { 00167 checkFile(*it); 00168 } 00169 updateFiles.clear(); 00170 } 00171 } 00172 00173 KonfUpdate::~KonfUpdate() 00174 { 00175 delete m_config; 00176 delete m_file; 00177 delete m_textStream; 00178 } 00179 00180 QTextStream & operator<<(QTextStream & stream, const QStringList & lst) 00181 { 00182 stream << lst.join(", "); 00183 return stream; 00184 } 00185 00186 QTextStream & 00187 KonfUpdate::log() 00188 { 00189 if (!m_textStream) { 00190 QString file = KStandardDirs::locateLocal("data", "kconf_update/log/update.log"); 00191 m_file = new QFile(file); 00192 if (m_file->open(QIODevice::WriteOnly | QIODevice::Append)) { 00193 m_textStream = new QTextStream(m_file); 00194 } else { 00195 // Error 00196 m_textStream = new QTextStream(stderr, QIODevice::WriteOnly); 00197 } 00198 } 00199 00200 (*m_textStream) << QDateTime::currentDateTime().toString(Qt::ISODate) << " "; 00201 00202 return *m_textStream; 00203 } 00204 00205 QTextStream & 00206 KonfUpdate::logFileError() 00207 { 00208 return log() << m_currentFilename << ':' << m_lineCount << ":'" << m_line << "': "; 00209 } 00210 00211 QStringList KonfUpdate::findUpdateFiles(bool dirtyOnly) 00212 { 00213 QStringList result; 00214 const QStringList list = KGlobal::dirs()->findAllResources("data", "kconf_update/*.upd", 00215 KStandardDirs::NoDuplicates); 00216 for (QStringList::ConstIterator it = list.constBegin(); 00217 it != list.constEnd(); 00218 ++it) { 00219 QString file = *it; 00220 KDE_struct_stat buff; 00221 if (KDE::stat(file, &buff) == 0) { 00222 int i = file.lastIndexOf('/'); 00223 if (i != -1) { 00224 file = file.mid(i + 1); 00225 } 00226 KConfigGroup cg(m_config, file); 00227 time_t ctime = cg.readEntry("ctime", 0); 00228 time_t mtime = cg.readEntry("mtime", 0); 00229 if (!dirtyOnly || 00230 (ctime != buff.st_ctime) || (mtime != buff.st_mtime)) { 00231 result.append(*it); 00232 } 00233 } 00234 } 00235 return result; 00236 } 00237 00238 bool KonfUpdate::checkFile(const QString &filename) 00239 { 00240 m_currentFilename = filename; 00241 int i = m_currentFilename.lastIndexOf('/'); 00242 if (i != -1) { 00243 m_currentFilename = m_currentFilename.mid(i + 1); 00244 } 00245 m_skip = true; 00246 QFile file(filename); 00247 if (!file.open(QIODevice::ReadOnly)) { 00248 return false; 00249 } 00250 00251 QTextStream ts(&file); 00252 ts.setCodec(QTextCodec::codecForName("ISO-8859-1")); 00253 int lineCount = 0; 00254 resetOptions(); 00255 QString id; 00256 while (!ts.atEnd()) { 00257 QString line = ts.readLine().trimmed(); 00258 lineCount++; 00259 if (line.isEmpty() || (line[0] == '#')) { 00260 continue; 00261 } 00262 if (line.startsWith("Id=")) { 00263 id = m_currentFilename + ':' + line.mid(3); 00264 } else if (line.startsWith("File=")) { 00265 checkGotFile(line.mid(5), id); 00266 } 00267 } 00268 00269 return true; 00270 } 00271 00272 void KonfUpdate::checkGotFile(const QString &_file, const QString &id) 00273 { 00274 QString file; 00275 int i = _file.indexOf(','); 00276 if (i == -1) { 00277 file = _file.trimmed(); 00278 } else { 00279 file = _file.mid(i + 1).trimmed(); 00280 } 00281 00282 // qDebug("File %s, id %s", file.toLatin1().constData(), id.toLatin1().constData()); 00283 00284 KConfig cfg(file, KConfig::SimpleConfig); 00285 KConfigGroup cg(&cfg, "$Version"); 00286 QStringList ids = cg.readEntry("update_info", QStringList()); 00287 if (ids.contains(id)) { 00288 return; 00289 } 00290 ids.append(id); 00291 cg.writeEntry("update_info", ids); 00292 } 00293 00313 bool KonfUpdate::updateFile(const QString &filename) 00314 { 00315 m_currentFilename = filename; 00316 int i = m_currentFilename.lastIndexOf('/'); 00317 if (i != -1) { 00318 m_currentFilename = m_currentFilename.mid(i + 1); 00319 } 00320 m_skip = true; 00321 QFile file(filename); 00322 if (!file.open(QIODevice::ReadOnly)) { 00323 return false; 00324 } 00325 00326 log() << "Checking update-file '" << filename << "' for new updates" << endl; 00327 00328 QTextStream ts(&file); 00329 ts.setCodec(QTextCodec::codecForName("ISO-8859-1")); 00330 m_lineCount = 0; 00331 resetOptions(); 00332 while (!ts.atEnd()) { 00333 m_line = ts.readLine().trimmed(); 00334 m_lineCount++; 00335 if (m_line.isEmpty() || (m_line[0] == '#')) { 00336 continue; 00337 } 00338 if (m_line.startsWith(QLatin1String("Id="))) { 00339 gotId(m_line.mid(3)); 00340 } else if (m_skip) { 00341 continue; 00342 } else if (m_line.startsWith(QLatin1String("Options="))) { 00343 gotOptions(m_line.mid(8)); 00344 } else if (m_line.startsWith(QLatin1String("File="))) { 00345 gotFile(m_line.mid(5)); 00346 } else if (m_skipFile) { 00347 continue; 00348 } else if (m_line.startsWith(QLatin1String("Group="))) { 00349 gotGroup(m_line.mid(6)); 00350 } else if (m_line.startsWith(QLatin1String("RemoveGroup="))) { 00351 gotRemoveGroup(m_line.mid(12)); 00352 resetOptions(); 00353 } else if (m_line.startsWith(QLatin1String("Script="))) { 00354 gotScript(m_line.mid(7)); 00355 resetOptions(); 00356 } else if (m_line.startsWith(QLatin1String("ScriptArguments="))) { 00357 gotScriptArguments(m_line.mid(16)); 00358 } else if (m_line.startsWith(QLatin1String("Key="))) { 00359 gotKey(m_line.mid(4)); 00360 resetOptions(); 00361 } else if (m_line.startsWith(QLatin1String("RemoveKey="))) { 00362 gotRemoveKey(m_line.mid(10)); 00363 resetOptions(); 00364 } else if (m_line == "AllKeys") { 00365 gotAllKeys(); 00366 resetOptions(); 00367 } else if (m_line == "AllGroups") { 00368 gotAllGroups(); 00369 resetOptions(); 00370 } else { 00371 logFileError() << "Parse error" << endl; 00372 } 00373 } 00374 // Flush. 00375 gotId(QString()); 00376 00377 KDE_struct_stat buff; 00378 KDE::stat(filename, &buff); 00379 KConfigGroup cg(m_config, m_currentFilename); 00380 cg.writeEntry("ctime", int(buff.st_ctime)); 00381 cg.writeEntry("mtime", int(buff.st_mtime)); 00382 cg.sync(); 00383 return true; 00384 } 00385 00386 00387 00388 void KonfUpdate::gotId(const QString &_id) 00389 { 00390 if (!m_id.isEmpty() && !m_skip) { 00391 KConfigGroup cg(m_config, m_currentFilename); 00392 00393 QStringList ids = cg.readEntry("done", QStringList()); 00394 if (!ids.contains(m_id)) { 00395 ids.append(m_id); 00396 cg.writeEntry("done", ids); 00397 cg.sync(); 00398 } 00399 } 00400 00401 // Flush pending changes 00402 gotFile(QString()); 00403 KConfigGroup cg(m_config, m_currentFilename); 00404 00405 QStringList ids = cg.readEntry("done", QStringList()); 00406 if (!_id.isEmpty()) { 00407 if (ids.contains(_id)) { 00408 //qDebug("Id '%s' was already in done-list", _id.toLatin1().constData()); 00409 if (!m_bUseConfigInfo) { 00410 m_skip = true; 00411 return; 00412 } 00413 } 00414 m_skip = false; 00415 m_skipFile = false; 00416 m_id = _id; 00417 if (m_bUseConfigInfo) { 00418 log() << m_currentFilename << ": Checking update '" << _id << "'" << endl; 00419 } else { 00420 log() << m_currentFilename << ": Found new update '" << _id << "'" << endl; 00421 } 00422 } 00423 } 00424 00425 void KonfUpdate::gotFile(const QString &_file) 00426 { 00427 // Reset group 00428 gotGroup(QString()); 00429 00430 if (!m_oldFile.isEmpty()) { 00431 // Close old file. 00432 delete m_oldConfig1; 00433 m_oldConfig1 = 0; 00434 00435 KConfigGroup cg(m_oldConfig2, "$Version"); 00436 QStringList ids = cg.readEntry("update_info", QStringList()); 00437 QString cfg_id = m_currentFilename + ':' + m_id; 00438 if (!ids.contains(cfg_id) && !m_skip) { 00439 ids.append(cfg_id); 00440 cg.writeEntry("update_info", ids); 00441 } 00442 cg.sync(); 00443 delete m_oldConfig2; 00444 m_oldConfig2 = 0; 00445 00446 QString file = KStandardDirs::locateLocal("config", m_oldFile); 00447 KDE_struct_stat s_buf; 00448 if (KDE::stat(file, &s_buf) == 0) { 00449 if (s_buf.st_size == 0) { 00450 // Delete empty file. 00451 QFile::remove(file); 00452 } 00453 } 00454 00455 m_oldFile.clear(); 00456 } 00457 if (!m_newFile.isEmpty()) { 00458 // Close new file. 00459 KConfigGroup cg(m_newConfig, "$Version"); 00460 QStringList ids = cg.readEntry("update_info", QStringList()); 00461 QString cfg_id = m_currentFilename + ':' + m_id; 00462 if (!ids.contains(cfg_id) && !m_skip) { 00463 ids.append(cfg_id); 00464 cg.writeEntry("update_info", ids); 00465 } 00466 m_newConfig->sync(); 00467 delete m_newConfig; 00468 m_newConfig = 0; 00469 00470 m_newFile.clear(); 00471 } 00472 m_newConfig = 0; 00473 00474 int i = _file.indexOf(','); 00475 if (i == -1) { 00476 m_oldFile = _file.trimmed(); 00477 } else { 00478 m_oldFile = _file.left(i).trimmed(); 00479 m_newFile = _file.mid(i + 1).trimmed(); 00480 if (m_oldFile == m_newFile) { 00481 m_newFile.clear(); 00482 } 00483 } 00484 00485 if (!m_oldFile.isEmpty()) { 00486 m_oldConfig2 = new KConfig(m_oldFile, KConfig::NoGlobals); 00487 QString cfg_id = m_currentFilename + ':' + m_id; 00488 KConfigGroup cg(m_oldConfig2, "$Version"); 00489 QStringList ids = cg.readEntry("update_info", QStringList()); 00490 if (ids.contains(cfg_id)) { 00491 m_skip = true; 00492 m_newFile.clear(); 00493 log() << m_currentFilename << ": Skipping update '" << m_id << "'" << endl; 00494 } 00495 00496 if (!m_newFile.isEmpty()) { 00497 m_newConfig = new KConfig(m_newFile, KConfig::NoGlobals); 00498 KConfigGroup cg(m_newConfig, "$Version"); 00499 ids = cg.readEntry("update_info", QStringList()); 00500 if (ids.contains(cfg_id)) { 00501 m_skip = true; 00502 log() << m_currentFilename << ": Skipping update '" << m_id << "'" << endl; 00503 } 00504 } else { 00505 m_newConfig = m_oldConfig2; 00506 } 00507 00508 m_oldConfig1 = new KConfig(m_oldFile, KConfig::NoGlobals); 00509 } else { 00510 m_newFile.clear(); 00511 } 00512 m_newFileName = m_newFile; 00513 if (m_newFileName.isEmpty()) { 00514 m_newFileName = m_oldFile; 00515 } 00516 00517 m_skipFile = false; 00518 if (!m_oldFile.isEmpty()) { // if File= is specified, it doesn't exist, is empty or contains only kconf_update's [$Version] group, skip 00519 if (m_oldConfig1 != NULL 00520 && (m_oldConfig1->groupList().isEmpty() 00521 || (m_oldConfig1->groupList().count() == 1 && m_oldConfig1->groupList().first() == "$Version"))) { 00522 log() << m_currentFilename << ": File '" << m_oldFile << "' does not exist or empty, skipping" << endl; 00523 m_skipFile = true; 00524 } 00525 } 00526 } 00527 00528 QStringList KonfUpdate::parseGroupString(const QString &str) 00529 { 00530 bool ok; 00531 QString error; 00532 QStringList lst = KConfigUtils::parseGroupString(str, &ok, &error); 00533 if (!ok) { 00534 logFileError() << error; 00535 } 00536 return lst; 00537 } 00538 00539 void KonfUpdate::gotGroup(const QString &_group) 00540 { 00541 QString group = _group.trimmed(); 00542 if (group.isEmpty()) { 00543 m_oldGroup = m_newGroup = QStringList(); 00544 return; 00545 } 00546 00547 QStringList tokens = group.split(','); 00548 m_oldGroup = parseGroupString(tokens.at(0)); 00549 if (tokens.count() == 1) { 00550 m_newGroup = m_oldGroup; 00551 } else { 00552 m_newGroup = parseGroupString(tokens.at(1)); 00553 } 00554 } 00555 00556 void KonfUpdate::gotRemoveGroup(const QString &_group) 00557 { 00558 m_oldGroup = parseGroupString(_group); 00559 00560 if (!m_oldConfig1) { 00561 logFileError() << "RemoveGroup without previous File specification" << endl; 00562 return; 00563 } 00564 00565 KConfigGroup cg = KConfigUtils::openGroup(m_oldConfig2, m_oldGroup); 00566 if (!cg.exists()) { 00567 return; 00568 } 00569 // Delete group. 00570 cg.deleteGroup(); 00571 log() << m_currentFilename << ": RemoveGroup removes group " << m_oldFile << ":" << m_oldGroup << endl; 00572 } 00573 00574 00575 void KonfUpdate::gotKey(const QString &_key) 00576 { 00577 QString oldKey, newKey; 00578 int i = _key.indexOf(','); 00579 if (i == -1) { 00580 oldKey = _key.trimmed(); 00581 newKey = oldKey; 00582 } else { 00583 oldKey = _key.left(i).trimmed(); 00584 newKey = _key.mid(i + 1).trimmed(); 00585 } 00586 00587 if (oldKey.isEmpty() || newKey.isEmpty()) { 00588 logFileError() << "Key specifies invalid key" << endl; 00589 return; 00590 } 00591 if (!m_oldConfig1) { 00592 logFileError() << "Key without previous File specification" << endl; 00593 return; 00594 } 00595 copyOrMoveKey(m_oldGroup, oldKey, m_newGroup, newKey); 00596 } 00597 00598 void KonfUpdate::copyOrMoveKey(const QStringList &srcGroupPath, const QString &srcKey, const QStringList &dstGroupPath, const QString &dstKey) 00599 { 00600 KConfigGroup dstCg = KConfigUtils::openGroup(m_newConfig, dstGroupPath); 00601 if (!m_bOverwrite && dstCg.hasKey(dstKey)) { 00602 log() << m_currentFilename << ": Skipping " << m_newFileName << ":" << dstCg.name() << ":" << dstKey << ", already exists." << endl; 00603 return; 00604 } 00605 00606 KConfigGroup srcCg = KConfigUtils::openGroup(m_oldConfig1, srcGroupPath); 00607 QString value = srcCg.readEntry(srcKey, QString()); 00608 log() << m_currentFilename << ": Updating " << m_newFileName << ":" << dstCg.name() << ":" << dstKey << " to '" << value << "'" << endl; 00609 dstCg.writeEntry(dstKey, value); 00610 00611 if (m_bCopy) { 00612 return; // Done. 00613 } 00614 00615 // Delete old entry 00616 if (m_oldConfig2 == m_newConfig 00617 && srcGroupPath == dstGroupPath 00618 && srcKey == dstKey) { 00619 return; // Don't delete! 00620 } 00621 KConfigGroup srcCg2 = KConfigUtils::openGroup(m_oldConfig2, srcGroupPath); 00622 srcCg2.deleteEntry(srcKey); 00623 log() << m_currentFilename << ": Removing " << m_oldFile << ":" << srcCg2.name() << ":" << srcKey << ", moved." << endl; 00624 } 00625 00626 void KonfUpdate::copyOrMoveGroup(const QStringList &srcGroupPath, const QStringList &dstGroupPath) 00627 { 00628 KConfigGroup cg = KConfigUtils::openGroup(m_oldConfig1, srcGroupPath); 00629 00630 // Keys 00631 Q_FOREACH(const QString &key, cg.keyList()) { 00632 copyOrMoveKey(srcGroupPath, key, dstGroupPath, key); 00633 } 00634 00635 // Subgroups 00636 Q_FOREACH(const QString &group, cg.groupList()) { 00637 QStringList groupPath = QStringList() << group; 00638 copyOrMoveGroup(srcGroupPath + groupPath, dstGroupPath + groupPath); 00639 } 00640 } 00641 00642 void KonfUpdate::gotRemoveKey(const QString &_key) 00643 { 00644 QString key = _key.trimmed(); 00645 00646 if (key.isEmpty()) { 00647 logFileError() << "RemoveKey specifies invalid key" << endl; 00648 return; 00649 } 00650 00651 if (!m_oldConfig1) { 00652 logFileError() << "Key without previous File specification" << endl; 00653 return; 00654 } 00655 00656 KConfigGroup cg1 = KConfigUtils::openGroup(m_oldConfig1, m_oldGroup); 00657 if (!cg1.hasKey(key)) { 00658 return; 00659 } 00660 log() << m_currentFilename << ": RemoveKey removes " << m_oldFile << ":" << m_oldGroup << ":" << key << endl; 00661 00662 // Delete old entry 00663 KConfigGroup cg2 = KConfigUtils::openGroup(m_oldConfig2, m_oldGroup); 00664 cg2.deleteEntry(key); 00665 /*if (m_oldConfig2->deleteGroup(m_oldGroup, KConfig::Normal)) { // Delete group if empty. 00666 log() << m_currentFilename << ": Removing empty group " << m_oldFile << ":" << m_oldGroup << endl; 00667 } (this should be automatic)*/ 00668 } 00669 00670 void KonfUpdate::gotAllKeys() 00671 { 00672 if (!m_oldConfig1) { 00673 logFileError() << "AllKeys without previous File specification" << endl; 00674 return; 00675 } 00676 00677 copyOrMoveGroup(m_oldGroup, m_newGroup); 00678 } 00679 00680 void KonfUpdate::gotAllGroups() 00681 { 00682 if (!m_oldConfig1) { 00683 logFileError() << "AllGroups without previous File specification" << endl; 00684 return; 00685 } 00686 00687 const QStringList allGroups = m_oldConfig1->groupList(); 00688 for (QStringList::ConstIterator it = allGroups.begin(); 00689 it != allGroups.end(); ++it) { 00690 m_oldGroup = QStringList() << *it; 00691 m_newGroup = m_oldGroup; 00692 gotAllKeys(); 00693 } 00694 } 00695 00696 void KonfUpdate::gotOptions(const QString &_options) 00697 { 00698 const QStringList options = _options.split(','); 00699 for (QStringList::ConstIterator it = options.begin(); 00700 it != options.end(); 00701 ++it) { 00702 if ((*it).toLower().trimmed() == "copy") { 00703 m_bCopy = true; 00704 } 00705 00706 if ((*it).toLower().trimmed() == "overwrite") { 00707 m_bOverwrite = true; 00708 } 00709 } 00710 } 00711 00712 void KonfUpdate::copyGroup(const KConfigBase *cfg1, const QString &group1, 00713 KConfigBase *cfg2, const QString &group2) 00714 { 00715 KConfigGroup cg1(cfg1, group1); 00716 KConfigGroup cg2(cfg2, group2); 00717 copyGroup(cg1, cg2); 00718 } 00719 00720 void KonfUpdate::copyGroup(const KConfigGroup &cg1, KConfigGroup &cg2) 00721 { 00722 // Copy keys 00723 QMap<QString, QString> list = cg1.entryMap(); 00724 for (QMap<QString, QString>::ConstIterator it = list.constBegin(); 00725 it != list.constEnd(); ++it) { 00726 if (m_bOverwrite || !cg2.hasKey(it.key())) { 00727 cg2.writeEntry(it.key(), it.value()); 00728 } 00729 } 00730 00731 // Copy subgroups 00732 Q_FOREACH(const QString &group, cg1.groupList()) { 00733 copyGroup(&cg1, group, &cg2, group); 00734 } 00735 } 00736 00737 void KonfUpdate::gotScriptArguments(const QString &_arguments) 00738 { 00739 m_arguments = _arguments; 00740 } 00741 00742 void KonfUpdate::gotScript(const QString &_script) 00743 { 00744 QString script, interpreter; 00745 int i = _script.indexOf(','); 00746 if (i == -1) { 00747 script = _script.trimmed(); 00748 } else { 00749 script = _script.left(i).trimmed(); 00750 interpreter = _script.mid(i + 1).trimmed(); 00751 } 00752 00753 00754 if (script.isEmpty()) { 00755 logFileError() << "Script fails to specify filename"; 00756 m_skip = true; 00757 return; 00758 } 00759 00760 00761 00762 QString path = KStandardDirs::locate("data", "kconf_update/" + script); 00763 if (path.isEmpty()) { 00764 if (interpreter.isEmpty()) { 00765 path = KStandardDirs::locate("lib", "kconf_update_bin/" + script); 00766 } 00767 00768 if (path.isEmpty()) { 00769 logFileError() << "Script '" << script << "' not found" << endl; 00770 m_skip = true; 00771 return; 00772 } 00773 } 00774 00775 if (!m_arguments.isNull()) { 00776 log() << m_currentFilename << ": Running script '" << script << "' with arguments '" << m_arguments << "'" << endl; 00777 } else { 00778 log() << m_currentFilename << ": Running script '" << script << "'" << endl; 00779 } 00780 00781 QString cmd; 00782 if (interpreter.isEmpty()) { 00783 cmd = path; 00784 } else { 00785 cmd = interpreter + ' ' + path; 00786 } 00787 00788 if (!m_arguments.isNull()) { 00789 cmd += ' '; 00790 cmd += m_arguments; 00791 } 00792 00793 KTemporaryFile scriptIn; 00794 scriptIn.open(); 00795 KTemporaryFile scriptOut; 00796 scriptOut.open(); 00797 KTemporaryFile scriptErr; 00798 scriptErr.open(); 00799 00800 int result; 00801 if (m_oldConfig1) { 00802 if (m_debug) { 00803 scriptIn.setAutoRemove(false); 00804 log() << "Script input stored in " << scriptIn.fileName() << endl; 00805 } 00806 KConfig cfg(scriptIn.fileName(), KConfig::SimpleConfig); 00807 00808 if (m_oldGroup.isEmpty()) { 00809 // Write all entries to tmpFile; 00810 const QStringList grpList = m_oldConfig1->groupList(); 00811 for (QStringList::ConstIterator it = grpList.begin(); 00812 it != grpList.end(); 00813 ++it) { 00814 copyGroup(m_oldConfig1, *it, &cfg, *it); 00815 } 00816 } else { 00817 KConfigGroup cg1 = KConfigUtils::openGroup(m_oldConfig1, m_oldGroup); 00818 KConfigGroup cg2(&cfg, QString()); 00819 copyGroup(cg1, cg2); 00820 } 00821 cfg.sync(); 00822 #ifndef _WIN32_WCE 00823 result = system(QFile::encodeName(QString("%1 < %2 > %3 2> %4").arg(cmd, scriptIn.fileName(), scriptOut.fileName(), scriptErr.fileName()))); 00824 #else 00825 QString path_ = QDir::convertSeparators ( QFileInfo ( cmd ).absoluteFilePath() ); 00826 QString file_ = QFileInfo ( cmd ).fileName(); 00827 SHELLEXECUTEINFO execInfo; 00828 memset ( &execInfo,0,sizeof ( execInfo ) ); 00829 execInfo.cbSize = sizeof ( execInfo ); 00830 execInfo.fMask = SEE_MASK_FLAG_NO_UI; 00831 execInfo.lpVerb = L"open"; 00832 execInfo.lpFile = (LPCWSTR) path_.utf16(); 00833 execInfo.lpDirectory = (LPCWSTR) file_.utf16(); 00834 execInfo.lpParameters = (LPCWSTR) QString(" < %1 > %2 2> %3").arg( scriptIn.fileName(), scriptOut.fileName(), scriptErr.fileName()).utf16(); 00835 result = ShellExecuteEx ( &execInfo ); 00836 if (result != 0) 00837 { 00838 result = 0; 00839 } 00840 else 00841 { 00842 result = -1; 00843 } 00844 #endif 00845 } else { 00846 // No config file 00847 #ifndef _WIN32_WCE 00848 result = system(QFile::encodeName(QString("%1 2> %2").arg(cmd, scriptErr.fileName()))); 00849 #else 00850 QString path_ = QDir::convertSeparators ( QFileInfo ( cmd ).absoluteFilePath() ); 00851 QString file_ = QFileInfo ( cmd ).fileName(); 00852 SHELLEXECUTEINFO execInfo; 00853 memset ( &execInfo,0,sizeof ( execInfo ) ); 00854 execInfo.cbSize = sizeof ( execInfo ); 00855 execInfo.fMask = SEE_MASK_FLAG_NO_UI; 00856 execInfo.lpVerb = L"open"; 00857 execInfo.lpFile = (LPCWSTR) path_.utf16(); 00858 execInfo.lpDirectory = (LPCWSTR) file_.utf16(); 00859 execInfo.lpParameters = (LPCWSTR) QString(" 2> %1").arg( scriptErr.fileName()).utf16(); 00860 result = ShellExecuteEx ( &execInfo ); 00861 if (result != 0) 00862 { 00863 result = 0; 00864 } 00865 else 00866 { 00867 result = -1; 00868 } 00869 #endif 00870 } 00871 00872 // Copy script stderr to log file 00873 { 00874 QFile output(scriptErr.fileName()); 00875 if (output.open(QIODevice::ReadOnly)) { 00876 QTextStream ts(&output); 00877 ts.setCodec(QTextCodec::codecForName("UTF-8")); 00878 while (!ts.atEnd()) { 00879 QString line = ts.readLine(); 00880 log() << "[Script] " << line << endl; 00881 } 00882 } 00883 } 00884 00885 if (result) { 00886 log() << m_currentFilename << ": !! An error occurred while running '" << cmd << "'" << endl; 00887 return; 00888 } 00889 00890 if (!m_oldConfig1) { 00891 return; // Nothing to merge 00892 } 00893 00894 if (m_debug) { 00895 scriptOut.setAutoRemove(false); 00896 log() << "Script output stored in " << scriptOut.fileName() << endl; 00897 } 00898 00899 // Deleting old entries 00900 { 00901 QStringList group = m_oldGroup; 00902 QFile output(scriptOut.fileName()); 00903 if (output.open(QIODevice::ReadOnly)) { 00904 QTextStream ts(&output); 00905 ts.setCodec(QTextCodec::codecForName("UTF-8")); 00906 while (!ts.atEnd()) { 00907 QString line = ts.readLine(); 00908 if (line.startsWith('[')) { 00909 group = parseGroupString(line); 00910 } else if (line.startsWith(QLatin1String("# DELETE "))) { 00911 QString key = line.mid(9); 00912 if (key[0] == '[') { 00913 int j = key.lastIndexOf(']') + 1; 00914 if (j > 0) { 00915 group = parseGroupString(key.left(j)); 00916 key = key.mid(j); 00917 } 00918 } 00919 KConfigGroup cg = KConfigUtils::openGroup(m_oldConfig2, group); 00920 cg.deleteEntry(key); 00921 log() << m_currentFilename << ": Script removes " << m_oldFile << ":" << group << ":" << key << endl; 00922 /*if (m_oldConfig2->deleteGroup(group, KConfig::Normal)) { // Delete group if empty. 00923 log() << m_currentFilename << ": Removing empty group " << m_oldFile << ":" << group << endl; 00924 } (this should be automatic)*/ 00925 } else if (line.startsWith(QLatin1String("# DELETEGROUP"))) { 00926 QString str = line.mid(13).trimmed(); 00927 if (!str.isEmpty()) { 00928 group = parseGroupString(str); 00929 } 00930 KConfigGroup cg = KConfigUtils::openGroup(m_oldConfig2, group); 00931 cg.deleteGroup(); 00932 log() << m_currentFilename << ": Script removes group " << m_oldFile << ":" << group << endl; 00933 } 00934 } 00935 } 00936 } 00937 00938 // Merging in new entries. 00939 KConfig scriptOutConfig(scriptOut.fileName(), KConfig::NoGlobals); 00940 if (m_newGroup.isEmpty()) { 00941 // Copy "default" keys as members of "default" keys 00942 copyGroup(&scriptOutConfig, QString(), m_newConfig, QString()); 00943 } else { 00944 // Copy default keys as members of m_newGroup 00945 KConfigGroup srcCg = KConfigUtils::openGroup(&scriptOutConfig, QStringList()); 00946 KConfigGroup dstCg = KConfigUtils::openGroup(m_newConfig, m_newGroup); 00947 copyGroup(srcCg, dstCg); 00948 } 00949 Q_FOREACH(const QString &group, scriptOutConfig.groupList()) { 00950 copyGroup(&scriptOutConfig, group, m_newConfig, group); 00951 } 00952 } 00953 00954 void KonfUpdate::resetOptions() 00955 { 00956 m_bCopy = false; 00957 m_bOverwrite = false; 00958 m_arguments.clear(); 00959 } 00960 00961 00962 extern "C" KDE_EXPORT int kdemain(int argc, char **argv) 00963 { 00964 KCmdLineOptions options; 00965 options.add("debug", ki18n("Keep output results from scripts")); 00966 options.add("check <update-file>", ki18n("Check whether config file itself requires updating")); 00967 options.add("+[file]", ki18n("File to read update instructions from")); 00968 00969 KAboutData aboutData("kconf_update", 0, ki18n("KConf Update"), 00970 "1.0.2", 00971 ki18n("KDE Tool for updating user configuration files"), 00972 KAboutData::License_GPL, 00973 ki18n("(c) 2001, Waldo Bastian")); 00974 00975 aboutData.addAuthor(ki18n("Waldo Bastian"), KLocalizedString(), "bastian@kde.org"); 00976 00977 KCmdLineArgs::init(argc, argv, &aboutData); 00978 KCmdLineArgs::addCmdLineOptions(options); 00979 00980 KComponentData componentData(&aboutData); 00981 00982 KonfUpdate konfUpdate; 00983 00984 return 0; 00985 }
KDE 4.6 API Reference