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.toLocalFile()); 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 if (!srcCg.hasKey(srcKey)) 00608 return; 00609 QString value = srcCg.readEntry(srcKey, QString()); 00610 log() << m_currentFilename << ": Updating " << m_newFileName << ":" << dstCg.name() << ":" << dstKey << " to '" << value << "'" << endl; 00611 dstCg.writeEntry(dstKey, value); 00612 00613 if (m_bCopy) { 00614 return; // Done. 00615 } 00616 00617 // Delete old entry 00618 if (m_oldConfig2 == m_newConfig 00619 && srcGroupPath == dstGroupPath 00620 && srcKey == dstKey) { 00621 return; // Don't delete! 00622 } 00623 KConfigGroup srcCg2 = KConfigUtils::openGroup(m_oldConfig2, srcGroupPath); 00624 srcCg2.deleteEntry(srcKey); 00625 log() << m_currentFilename << ": Removing " << m_oldFile << ":" << srcCg2.name() << ":" << srcKey << ", moved." << endl; 00626 } 00627 00628 void KonfUpdate::copyOrMoveGroup(const QStringList &srcGroupPath, const QStringList &dstGroupPath) 00629 { 00630 KConfigGroup cg = KConfigUtils::openGroup(m_oldConfig1, srcGroupPath); 00631 00632 // Keys 00633 Q_FOREACH(const QString &key, cg.keyList()) { 00634 copyOrMoveKey(srcGroupPath, key, dstGroupPath, key); 00635 } 00636 00637 // Subgroups 00638 Q_FOREACH(const QString &group, cg.groupList()) { 00639 QStringList groupPath = QStringList() << group; 00640 copyOrMoveGroup(srcGroupPath + groupPath, dstGroupPath + groupPath); 00641 } 00642 } 00643 00644 void KonfUpdate::gotRemoveKey(const QString &_key) 00645 { 00646 QString key = _key.trimmed(); 00647 00648 if (key.isEmpty()) { 00649 logFileError() << "RemoveKey specifies invalid key" << endl; 00650 return; 00651 } 00652 00653 if (!m_oldConfig1) { 00654 logFileError() << "Key without previous File specification" << endl; 00655 return; 00656 } 00657 00658 KConfigGroup cg1 = KConfigUtils::openGroup(m_oldConfig1, m_oldGroup); 00659 if (!cg1.hasKey(key)) { 00660 return; 00661 } 00662 log() << m_currentFilename << ": RemoveKey removes " << m_oldFile << ":" << m_oldGroup << ":" << key << endl; 00663 00664 // Delete old entry 00665 KConfigGroup cg2 = KConfigUtils::openGroup(m_oldConfig2, m_oldGroup); 00666 cg2.deleteEntry(key); 00667 /*if (m_oldConfig2->deleteGroup(m_oldGroup, KConfig::Normal)) { // Delete group if empty. 00668 log() << m_currentFilename << ": Removing empty group " << m_oldFile << ":" << m_oldGroup << endl; 00669 } (this should be automatic)*/ 00670 } 00671 00672 void KonfUpdate::gotAllKeys() 00673 { 00674 if (!m_oldConfig1) { 00675 logFileError() << "AllKeys without previous File specification" << endl; 00676 return; 00677 } 00678 00679 copyOrMoveGroup(m_oldGroup, m_newGroup); 00680 } 00681 00682 void KonfUpdate::gotAllGroups() 00683 { 00684 if (!m_oldConfig1) { 00685 logFileError() << "AllGroups without previous File specification" << endl; 00686 return; 00687 } 00688 00689 const QStringList allGroups = m_oldConfig1->groupList(); 00690 for (QStringList::ConstIterator it = allGroups.begin(); 00691 it != allGroups.end(); ++it) { 00692 m_oldGroup = QStringList() << *it; 00693 m_newGroup = m_oldGroup; 00694 gotAllKeys(); 00695 } 00696 } 00697 00698 void KonfUpdate::gotOptions(const QString &_options) 00699 { 00700 const QStringList options = _options.split(','); 00701 for (QStringList::ConstIterator it = options.begin(); 00702 it != options.end(); 00703 ++it) { 00704 if ((*it).toLower().trimmed() == "copy") { 00705 m_bCopy = true; 00706 } 00707 00708 if ((*it).toLower().trimmed() == "overwrite") { 00709 m_bOverwrite = true; 00710 } 00711 } 00712 } 00713 00714 void KonfUpdate::copyGroup(const KConfigBase *cfg1, const QString &group1, 00715 KConfigBase *cfg2, const QString &group2) 00716 { 00717 KConfigGroup cg1(cfg1, group1); 00718 KConfigGroup cg2(cfg2, group2); 00719 copyGroup(cg1, cg2); 00720 } 00721 00722 void KonfUpdate::copyGroup(const KConfigGroup &cg1, KConfigGroup &cg2) 00723 { 00724 // Copy keys 00725 QMap<QString, QString> list = cg1.entryMap(); 00726 for (QMap<QString, QString>::ConstIterator it = list.constBegin(); 00727 it != list.constEnd(); ++it) { 00728 if (m_bOverwrite || !cg2.hasKey(it.key())) { 00729 cg2.writeEntry(it.key(), it.value()); 00730 } 00731 } 00732 00733 // Copy subgroups 00734 Q_FOREACH(const QString &group, cg1.groupList()) { 00735 copyGroup(&cg1, group, &cg2, group); 00736 } 00737 } 00738 00739 void KonfUpdate::gotScriptArguments(const QString &_arguments) 00740 { 00741 m_arguments = _arguments; 00742 } 00743 00744 void KonfUpdate::gotScript(const QString &_script) 00745 { 00746 QString script, interpreter; 00747 int i = _script.indexOf(','); 00748 if (i == -1) { 00749 script = _script.trimmed(); 00750 } else { 00751 script = _script.left(i).trimmed(); 00752 interpreter = _script.mid(i + 1).trimmed(); 00753 } 00754 00755 00756 if (script.isEmpty()) { 00757 logFileError() << "Script fails to specify filename"; 00758 m_skip = true; 00759 return; 00760 } 00761 00762 00763 00764 QString path = KStandardDirs::locate("data", "kconf_update/" + script); 00765 if (path.isEmpty()) { 00766 if (interpreter.isEmpty()) { 00767 path = KStandardDirs::locate("lib", "kconf_update_bin/" + script); 00768 } 00769 00770 if (path.isEmpty()) { 00771 logFileError() << "Script '" << script << "' not found" << endl; 00772 m_skip = true; 00773 return; 00774 } 00775 } 00776 00777 if (!m_arguments.isNull()) { 00778 log() << m_currentFilename << ": Running script '" << script << "' with arguments '" << m_arguments << "'" << endl; 00779 } else { 00780 log() << m_currentFilename << ": Running script '" << script << "'" << endl; 00781 } 00782 00783 QString cmd; 00784 if (interpreter.isEmpty()) { 00785 cmd = path; 00786 } else { 00787 cmd = interpreter + ' ' + path; 00788 } 00789 00790 if (!m_arguments.isNull()) { 00791 cmd += ' '; 00792 cmd += m_arguments; 00793 } 00794 00795 KTemporaryFile scriptIn; 00796 scriptIn.open(); 00797 KTemporaryFile scriptOut; 00798 scriptOut.open(); 00799 KTemporaryFile scriptErr; 00800 scriptErr.open(); 00801 00802 int result; 00803 if (m_oldConfig1) { 00804 if (m_debug) { 00805 scriptIn.setAutoRemove(false); 00806 log() << "Script input stored in " << scriptIn.fileName() << endl; 00807 } 00808 KConfig cfg(scriptIn.fileName(), KConfig::SimpleConfig); 00809 00810 if (m_oldGroup.isEmpty()) { 00811 // Write all entries to tmpFile; 00812 const QStringList grpList = m_oldConfig1->groupList(); 00813 for (QStringList::ConstIterator it = grpList.begin(); 00814 it != grpList.end(); 00815 ++it) { 00816 copyGroup(m_oldConfig1, *it, &cfg, *it); 00817 } 00818 } else { 00819 KConfigGroup cg1 = KConfigUtils::openGroup(m_oldConfig1, m_oldGroup); 00820 KConfigGroup cg2(&cfg, QString()); 00821 copyGroup(cg1, cg2); 00822 } 00823 cfg.sync(); 00824 #ifndef _WIN32_WCE 00825 result = system(QFile::encodeName(QString("%1 < %2 > %3 2> %4").arg(cmd, scriptIn.fileName(), scriptOut.fileName(), scriptErr.fileName()))); 00826 #else 00827 QString path_ = QDir::convertSeparators ( QFileInfo ( cmd ).absoluteFilePath() ); 00828 QString file_ = QFileInfo ( cmd ).fileName(); 00829 SHELLEXECUTEINFO execInfo; 00830 memset ( &execInfo,0,sizeof ( execInfo ) ); 00831 execInfo.cbSize = sizeof ( execInfo ); 00832 execInfo.fMask = SEE_MASK_FLAG_NO_UI; 00833 execInfo.lpVerb = L"open"; 00834 execInfo.lpFile = (LPCWSTR) path_.utf16(); 00835 execInfo.lpDirectory = (LPCWSTR) file_.utf16(); 00836 execInfo.lpParameters = (LPCWSTR) QString(" < %1 > %2 2> %3").arg( scriptIn.fileName(), scriptOut.fileName(), scriptErr.fileName()).utf16(); 00837 result = ShellExecuteEx ( &execInfo ); 00838 if (result != 0) 00839 { 00840 result = 0; 00841 } 00842 else 00843 { 00844 result = -1; 00845 } 00846 #endif 00847 } else { 00848 // No config file 00849 #ifndef _WIN32_WCE 00850 result = system(QFile::encodeName(QString("%1 2> %2").arg(cmd, scriptErr.fileName()))); 00851 #else 00852 QString path_ = QDir::convertSeparators ( QFileInfo ( cmd ).absoluteFilePath() ); 00853 QString file_ = QFileInfo ( cmd ).fileName(); 00854 SHELLEXECUTEINFO execInfo; 00855 memset ( &execInfo,0,sizeof ( execInfo ) ); 00856 execInfo.cbSize = sizeof ( execInfo ); 00857 execInfo.fMask = SEE_MASK_FLAG_NO_UI; 00858 execInfo.lpVerb = L"open"; 00859 execInfo.lpFile = (LPCWSTR) path_.utf16(); 00860 execInfo.lpDirectory = (LPCWSTR) file_.utf16(); 00861 execInfo.lpParameters = (LPCWSTR) QString(" 2> %1").arg( scriptErr.fileName()).utf16(); 00862 result = ShellExecuteEx ( &execInfo ); 00863 if (result != 0) 00864 { 00865 result = 0; 00866 } 00867 else 00868 { 00869 result = -1; 00870 } 00871 #endif 00872 } 00873 00874 // Copy script stderr to log file 00875 { 00876 QFile output(scriptErr.fileName()); 00877 if (output.open(QIODevice::ReadOnly)) { 00878 QTextStream ts(&output); 00879 ts.setCodec(QTextCodec::codecForName("UTF-8")); 00880 while (!ts.atEnd()) { 00881 QString line = ts.readLine(); 00882 log() << "[Script] " << line << endl; 00883 } 00884 } 00885 } 00886 00887 if (result) { 00888 log() << m_currentFilename << ": !! An error occurred while running '" << cmd << "'" << endl; 00889 return; 00890 } 00891 00892 if (!m_oldConfig1) { 00893 return; // Nothing to merge 00894 } 00895 00896 if (m_debug) { 00897 scriptOut.setAutoRemove(false); 00898 log() << "Script output stored in " << scriptOut.fileName() << endl; 00899 } 00900 00901 // Deleting old entries 00902 { 00903 QStringList group = m_oldGroup; 00904 QFile output(scriptOut.fileName()); 00905 if (output.open(QIODevice::ReadOnly)) { 00906 QTextStream ts(&output); 00907 ts.setCodec(QTextCodec::codecForName("UTF-8")); 00908 while (!ts.atEnd()) { 00909 QString line = ts.readLine(); 00910 if (line.startsWith('[')) { 00911 group = parseGroupString(line); 00912 } else if (line.startsWith(QLatin1String("# DELETE "))) { 00913 QString key = line.mid(9); 00914 if (key[0] == '[') { 00915 int j = key.lastIndexOf(']') + 1; 00916 if (j > 0) { 00917 group = parseGroupString(key.left(j)); 00918 key = key.mid(j); 00919 } 00920 } 00921 KConfigGroup cg = KConfigUtils::openGroup(m_oldConfig2, group); 00922 cg.deleteEntry(key); 00923 log() << m_currentFilename << ": Script removes " << m_oldFile << ":" << group << ":" << key << endl; 00924 /*if (m_oldConfig2->deleteGroup(group, KConfig::Normal)) { // Delete group if empty. 00925 log() << m_currentFilename << ": Removing empty group " << m_oldFile << ":" << group << endl; 00926 } (this should be automatic)*/ 00927 } else if (line.startsWith(QLatin1String("# DELETEGROUP"))) { 00928 QString str = line.mid(13).trimmed(); 00929 if (!str.isEmpty()) { 00930 group = parseGroupString(str); 00931 } 00932 KConfigGroup cg = KConfigUtils::openGroup(m_oldConfig2, group); 00933 cg.deleteGroup(); 00934 log() << m_currentFilename << ": Script removes group " << m_oldFile << ":" << group << endl; 00935 } 00936 } 00937 } 00938 } 00939 00940 // Merging in new entries. 00941 KConfig scriptOutConfig(scriptOut.fileName(), KConfig::NoGlobals); 00942 if (m_newGroup.isEmpty()) { 00943 // Copy "default" keys as members of "default" keys 00944 copyGroup(&scriptOutConfig, QString(), m_newConfig, QString()); 00945 } else { 00946 // Copy default keys as members of m_newGroup 00947 KConfigGroup srcCg = KConfigUtils::openGroup(&scriptOutConfig, QStringList()); 00948 KConfigGroup dstCg = KConfigUtils::openGroup(m_newConfig, m_newGroup); 00949 copyGroup(srcCg, dstCg); 00950 } 00951 Q_FOREACH(const QString &group, scriptOutConfig.groupList()) { 00952 copyGroup(&scriptOutConfig, group, m_newConfig, group); 00953 } 00954 } 00955 00956 void KonfUpdate::resetOptions() 00957 { 00958 m_bCopy = false; 00959 m_bOverwrite = false; 00960 m_arguments.clear(); 00961 } 00962 00963 00964 extern "C" KDE_EXPORT int kdemain(int argc, char **argv) 00965 { 00966 KCmdLineOptions options; 00967 options.add("debug", ki18n("Keep output results from scripts")); 00968 options.add("check <update-file>", ki18n("Check whether config file itself requires updating")); 00969 options.add("+[file]", ki18n("File to read update instructions from")); 00970 00971 KAboutData aboutData("kconf_update", 0, ki18n("KConf Update"), 00972 "1.0.2", 00973 ki18n("KDE Tool for updating user configuration files"), 00974 KAboutData::License_GPL, 00975 ki18n("(c) 2001, Waldo Bastian")); 00976 00977 aboutData.addAuthor(ki18n("Waldo Bastian"), KLocalizedString(), "bastian@kde.org"); 00978 00979 KCmdLineArgs::init(argc, argv, &aboutData); 00980 KCmdLineArgs::addCmdLineOptions(options); 00981 00982 KComponentData componentData(&aboutData); 00983 00984 KonfUpdate konfUpdate; 00985 00986 return 0; 00987 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:00 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:28:00 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.