21 #include <sys/types.h>
27 #include <QtCore/QDate>
28 #include <QtCore/QFile>
29 #include <QtCore/QTextStream>
30 #include <QtCore/QTextCodec>
32 #include <QtCore/QDir>
36 #include <kconfiggroup.h>
39 #include <kcmdlineargs.h>
41 #include <kstandarddirs.h>
42 #include <kaboutdata.h>
43 #include <kcomponentdata.h>
44 #include <ktemporaryfile.h>
59 bool checkFile(
const QString &filename);
62 bool updateFile(
const QString &filename);
65 void gotFile(
const QString &_file);
66 void gotGroup(
const QString &_group);
67 void gotRemoveGroup(
const QString &_group);
68 void gotKey(
const QString &_key);
69 void gotRemoveKey(
const QString &_key);
72 void gotOptions(
const QString &_options);
73 void gotScript(
const QString &_script);
74 void gotScriptArguments(
const QString &_arguments);
77 void copyGroup(
const KConfigBase *cfg1,
const QString &group1,
78 KConfigBase *cfg2,
const QString &group2);
79 void copyGroup(
const KConfigGroup &cg1, KConfigGroup &cg2);
96 KConfig *m_oldConfig1;
97 KConfig *m_oldConfig2;
105 bool m_bUseConfigInfo;
113 KonfUpdate::KonfUpdate()
114 : m_textStream(0), m_file(0)
116 bool updateAll =
false;
121 m_config =
new KConfig(
"kconf_updaterc");
122 KConfigGroup cg(m_config,
QString());
125 KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
127 m_debug = args->isSet(
"debug");
129 m_bUseConfigInfo =
false;
130 if (args->isSet(
"check")) {
131 m_bUseConfigInfo =
true;
132 QString file = KStandardDirs::locate(
"data",
"kconf_update/" + args->getOption(
"check"));
134 qWarning(
"File '%s' not found.", args->getOption(
"check").toLocal8Bit().data());
135 log() <<
"File '" << args->getOption(
"check") <<
"' passed on command line not found" << endl;
139 }
else if (args->count()) {
140 for (
int i = 0; i < args->count(); i++) {
141 KUrl url = args->url(i);
142 if (!url.isLocalFile()) {
143 KCmdLineArgs::usageError(i18n(
"Only local files are supported."));
145 updateFiles.
append(url.toLocalFile());
148 if (cg.readEntry(
"autoUpdateDisabled",
false))
150 updateFiles = findUpdateFiles(
true);
160 if (updateAll && !cg.readEntry(
"updateInfoAdded",
false)) {
161 cg.writeEntry(
"updateInfoAdded",
true);
162 updateFiles = findUpdateFiles(
false);
173 KonfUpdate::~KonfUpdate()
182 stream << lst.
join(
", ");
190 QString file = KStandardDirs::locateLocal(
"data",
"kconf_update/log/update.log");
191 m_file =
new QFile(file);
192 if (m_file->open(QIODevice::WriteOnly | QIODevice::Append)) {
196 m_textStream =
new QTextStream(stderr, QIODevice::WriteOnly);
202 return *m_textStream;
206 KonfUpdate::logFileError()
208 return log() << m_currentFilename <<
':' << m_lineCount <<
":'" << m_line <<
"': ";
211 QStringList KonfUpdate::findUpdateFiles(
bool dirtyOnly)
214 const QStringList list = KGlobal::dirs()->findAllResources(
"data",
"kconf_update/*.upd",
215 KStandardDirs::NoDuplicates);
220 KDE_struct_stat buff;
221 if (KDE::stat(file, &buff) == 0) {
224 file = file.
mid(i + 1);
226 KConfigGroup cg(m_config, file);
227 time_t ctime = cg.readEntry(
"ctime", 0);
228 time_t mtime = cg.readEntry(
"mtime", 0);
230 (ctime != buff.st_ctime) || (mtime != buff.st_mtime)) {
238 bool KonfUpdate::checkFile(
const QString &filename)
240 m_currentFilename = filename;
243 m_currentFilename = m_currentFilename.mid(i + 1);
246 QFile file(filename);
247 if (!file.open(QIODevice::ReadOnly)) {
256 while (!ts.atEnd()) {
259 if (line.
isEmpty() || (line[0] ==
'#')) {
263 id = m_currentFilename +
':' + line.
mid(3);
265 checkGotFile(line.
mid(5), id);
272 void KonfUpdate::checkGotFile(
const QString &_file,
const QString &
id)
284 KConfig cfg(file, KConfig::SimpleConfig);
285 KConfigGroup cg(&cfg,
"$Version");
291 cg.writeEntry(
"update_info", ids);
313 bool KonfUpdate::updateFile(
const QString &filename)
315 m_currentFilename = filename;
318 m_currentFilename = m_currentFilename.mid(i + 1);
321 QFile file(filename);
322 if (!file.open(QIODevice::ReadOnly)) {
326 log() <<
"Checking update-file '" << filename <<
"' for new updates" << endl;
332 while (!ts.atEnd()) {
333 m_line = ts.readLine().trimmed();
335 if (m_line.isEmpty() || (m_line[0] ==
'#')) {
339 gotId(m_line.mid(3));
343 gotOptions(m_line.mid(8));
345 gotFile(m_line.mid(5));
346 }
else if (m_skipFile) {
349 gotGroup(m_line.mid(6));
350 }
else if (m_line.startsWith(
QLatin1String(
"RemoveGroup="))) {
351 gotRemoveGroup(m_line.mid(12));
354 gotScript(m_line.mid(7));
356 }
else if (m_line.startsWith(
QLatin1String(
"ScriptArguments="))) {
357 gotScriptArguments(m_line.mid(16));
359 gotKey(m_line.mid(4));
362 gotRemoveKey(m_line.mid(10));
364 }
else if (m_line ==
"AllKeys") {
367 }
else if (m_line ==
"AllGroups") {
371 logFileError() <<
"Parse error" << endl;
377 KDE_struct_stat buff;
378 if (KDE::stat(filename, &buff) == 0) {
379 KConfigGroup cg(m_config, m_currentFilename);
380 cg.writeEntry(
"ctime",
int(buff.st_ctime));
381 cg.writeEntry(
"mtime",
int(buff.st_mtime));
389 void KonfUpdate::gotId(
const QString &_id)
391 if (!m_id.isEmpty() && !m_skip) {
392 KConfigGroup cg(m_config, m_currentFilename);
397 cg.writeEntry(
"done", ids);
404 KConfigGroup cg(m_config, m_currentFilename);
410 if (!m_bUseConfigInfo) {
418 if (m_bUseConfigInfo) {
419 log() << m_currentFilename <<
": Checking update '" << _id <<
"'" << endl;
421 log() << m_currentFilename <<
": Found new update '" << _id <<
"'" << endl;
426 void KonfUpdate::gotFile(
const QString &_file)
431 if (!m_oldFile.isEmpty()) {
436 KConfigGroup cg(m_oldConfig2,
"$Version");
438 QString cfg_id = m_currentFilename +
':' + m_id;
439 if (!ids.
contains(cfg_id) && !m_skip) {
441 cg.writeEntry(
"update_info", ids);
447 QString file = KStandardDirs::locateLocal(
"config", m_oldFile);
448 KDE_struct_stat s_buf;
449 if (KDE::stat(file, &s_buf) == 0) {
450 if (s_buf.st_size == 0) {
458 if (!m_newFile.isEmpty()) {
460 KConfigGroup cg(m_newConfig,
"$Version");
462 QString cfg_id = m_currentFilename +
':' + m_id;
463 if (!ids.
contains(cfg_id) && !m_skip) {
465 cg.writeEntry(
"update_info", ids);
481 if (m_oldFile == m_newFile) {
486 if (!m_oldFile.isEmpty()) {
487 m_oldConfig2 =
new KConfig(m_oldFile, KConfig::NoGlobals);
488 QString cfg_id = m_currentFilename +
':' + m_id;
489 KConfigGroup cg(m_oldConfig2,
"$Version");
494 log() << m_currentFilename <<
": Skipping update '" << m_id <<
"'" << endl;
497 if (!m_newFile.isEmpty()) {
498 m_newConfig =
new KConfig(m_newFile, KConfig::NoGlobals);
499 KConfigGroup cg(m_newConfig,
"$Version");
503 log() << m_currentFilename <<
": Skipping update '" << m_id <<
"'" << endl;
506 m_newConfig = m_oldConfig2;
509 m_oldConfig1 =
new KConfig(m_oldFile, KConfig::NoGlobals);
513 m_newFileName = m_newFile;
514 if (m_newFileName.isEmpty()) {
515 m_newFileName = m_oldFile;
519 if (!m_oldFile.isEmpty()) {
520 if (m_oldConfig1 != NULL
521 && (m_oldConfig1->groupList().isEmpty()
522 || (m_oldConfig1->groupList().count() == 1 && m_oldConfig1->groupList().first() ==
"$Version"))) {
523 log() << m_currentFilename <<
": File '" << m_oldFile <<
"' does not exist or empty, skipping" << endl;
535 logFileError() << error;
540 void KonfUpdate::gotGroup(
const QString &_group)
550 if (tokens.
count() == 1) {
551 m_newGroup = m_oldGroup;
557 void KonfUpdate::gotRemoveGroup(
const QString &_group)
562 logFileError() <<
"RemoveGroup without previous File specification" << endl;
572 log() << m_currentFilename <<
": RemoveGroup removes group " << m_oldFile <<
":" << m_oldGroup << endl;
576 void KonfUpdate::gotKey(
const QString &_key)
589 logFileError() <<
"Key specifies invalid key" << endl;
593 logFileError() <<
"Key without previous File specification" << endl;
596 copyOrMoveKey(m_oldGroup, oldKey, m_newGroup, newKey);
602 if (!m_bOverwrite && dstCg.hasKey(dstKey)) {
603 log() << m_currentFilename <<
": Skipping " << m_newFileName <<
":" << dstCg.name() <<
":" << dstKey <<
", already exists." << endl;
608 if (!srcCg.hasKey(srcKey))
611 log() << m_currentFilename <<
": Updating " << m_newFileName <<
":" << dstCg.name() <<
":" << dstKey <<
" to '" << value <<
"'" << endl;
612 dstCg.writeEntry(dstKey, value);
619 if (m_oldConfig2 == m_newConfig
620 && srcGroupPath == dstGroupPath
621 && srcKey == dstKey) {
625 srcCg2.deleteEntry(srcKey);
626 log() << m_currentFilename <<
": Removing " << m_oldFile <<
":" << srcCg2.name() <<
":" << srcKey <<
", moved." << endl;
634 Q_FOREACH(
const QString &key, cg.keyList()) {
635 copyOrMoveKey(srcGroupPath, key, dstGroupPath, key);
639 Q_FOREACH(
const QString &group, cg.groupList()) {
641 copyOrMoveGroup(srcGroupPath + groupPath, dstGroupPath + groupPath);
645 void KonfUpdate::gotRemoveKey(
const QString &_key)
650 logFileError() <<
"RemoveKey specifies invalid key" << endl;
655 logFileError() <<
"Key without previous File specification" << endl;
660 if (!cg1.hasKey(key)) {
663 log() << m_currentFilename <<
": RemoveKey removes " << m_oldFile <<
":" << m_oldGroup <<
":" << key << endl;
667 cg2.deleteEntry(key);
673 void KonfUpdate::gotAllKeys()
676 logFileError() <<
"AllKeys without previous File specification" << endl;
680 copyOrMoveGroup(m_oldGroup, m_newGroup);
683 void KonfUpdate::gotAllGroups()
686 logFileError() <<
"AllGroups without previous File specification" << endl;
690 const QStringList allGroups = m_oldConfig1->groupList();
692 it != allGroups.
end(); ++it) {
694 m_newGroup = m_oldGroup;
699 void KonfUpdate::gotOptions(
const QString &_options)
705 if ((*it).toLower().trimmed() ==
"copy") {
709 if ((*it).toLower().trimmed() ==
"overwrite") {
715 void KonfUpdate::copyGroup(
const KConfigBase *cfg1,
const QString &group1,
716 KConfigBase *cfg2,
const QString &group2)
718 KConfigGroup cg1(cfg1, group1);
719 KConfigGroup cg2(cfg2, group2);
723 void KonfUpdate::copyGroup(
const KConfigGroup &cg1, KConfigGroup &cg2)
729 if (m_bOverwrite || !cg2.hasKey(it.key())) {
730 cg2.writeEntry(it.key(), it.value());
735 Q_FOREACH(
const QString &group, cg1.groupList()) {
736 copyGroup(&cg1, group, &cg2, group);
740 void KonfUpdate::gotScriptArguments(
const QString &_arguments)
742 m_arguments = _arguments;
745 void KonfUpdate::gotScript(
const QString &_script)
758 logFileError() <<
"Script fails to specify filename";
765 QString path = KStandardDirs::locate(
"data",
"kconf_update/" + script);
768 path = KStandardDirs::locate(
"lib",
"kconf_update_bin/" + script);
772 logFileError() <<
"Script '" << script <<
"' not found" << endl;
778 if (!m_arguments.isNull()) {
779 log() << m_currentFilename <<
": Running script '" << script <<
"' with arguments '" << m_arguments <<
"'" << endl;
781 log() << m_currentFilename <<
": Running script '" << script <<
"'" << endl;
788 cmd = interpreter +
' ' + path;
791 if (!m_arguments.isNull()) {
796 KTemporaryFile scriptIn;
798 KTemporaryFile scriptOut;
800 KTemporaryFile scriptErr;
806 scriptIn.setAutoRemove(
false);
807 log() <<
"Script input stored in " << scriptIn.fileName() << endl;
809 KConfig cfg(scriptIn.fileName(), KConfig::SimpleConfig);
811 if (m_oldGroup.isEmpty()) {
813 const QStringList grpList = m_oldConfig1->groupList();
817 copyGroup(m_oldConfig1, *it, &cfg, *it);
821 KConfigGroup cg2(&cfg,
QString());
826 result = system(
QFile::encodeName(
QString(
"%1 < %2 > %3 2> %4").arg(cmd, scriptIn.fileName(), scriptOut.fileName(), scriptErr.fileName())));
830 SHELLEXECUTEINFO execInfo;
831 memset ( &execInfo,0,
sizeof ( execInfo ) );
832 execInfo.cbSize =
sizeof ( execInfo );
833 execInfo.fMask = SEE_MASK_FLAG_NO_UI;
834 execInfo.lpVerb = L
"open";
835 execInfo.lpFile = (LPCWSTR) path_.utf16();
836 execInfo.lpDirectory = (LPCWSTR) file_.
utf16();
837 execInfo.lpParameters = (LPCWSTR)
QString(
" < %1 > %2 2> %3").
arg( scriptIn.fileName(), scriptOut.fileName(), scriptErr.fileName()).utf16();
838 result = ShellExecuteEx ( &execInfo );
855 SHELLEXECUTEINFO execInfo;
856 memset ( &execInfo,0,
sizeof ( execInfo ) );
857 execInfo.cbSize =
sizeof ( execInfo );
858 execInfo.fMask = SEE_MASK_FLAG_NO_UI;
859 execInfo.lpVerb = L
"open";
860 execInfo.lpFile = (LPCWSTR) path_.utf16();
861 execInfo.lpDirectory = (LPCWSTR) file_.
utf16();
862 execInfo.lpParameters = (LPCWSTR)
QString(
" 2> %1").
arg( scriptErr.fileName()).utf16();
863 result = ShellExecuteEx ( &execInfo );
877 QFile output(scriptErr.fileName());
878 if (output.open(QIODevice::ReadOnly)) {
881 while (!ts.atEnd()) {
883 log() <<
"[Script] " << line << endl;
889 log() << m_currentFilename <<
": !! An error occurred while running '" << cmd <<
"'" << endl;
898 scriptOut.setAutoRemove(
false);
899 log() <<
"Script output stored in " << scriptOut.fileName() << endl;
905 QFile output(scriptOut.fileName());
906 if (output.open(QIODevice::ReadOnly)) {
909 while (!ts.atEnd()) {
924 log() << m_currentFilename <<
": Script removes " << m_oldFile <<
":" << group <<
":" << key << endl;
935 log() << m_currentFilename <<
": Script removes group " << m_oldFile <<
":" << group << endl;
942 KConfig scriptOutConfig(scriptOut.fileName(), KConfig::NoGlobals);
943 if (m_newGroup.isEmpty()) {
950 copyGroup(srcCg, dstCg);
952 Q_FOREACH(
const QString &group, scriptOutConfig.groupList()) {
953 copyGroup(&scriptOutConfig, group, m_newConfig, group);
957 void KonfUpdate::resetOptions()
960 m_bOverwrite =
false;
965 extern "C" KDE_EXPORT
int kdemain(
int argc,
char **argv)
967 KCmdLineOptions options;
968 options.add(
"debug", ki18n(
"Keep output results from scripts"));
969 options.add(
"check <update-file>", ki18n(
"Check whether config file itself requires updating"));
970 options.add(
"+[file]", ki18n(
"File to read update instructions from"));
972 KAboutData aboutData(
"kconf_update", 0, ki18n(
"KConf Update"),
974 ki18n(
"KDE Tool for updating user configuration files"),
975 KAboutData::License_GPL,
976 ki18n(
"(c) 2001, Waldo Bastian"));
978 aboutData.addAuthor(ki18n(
"Waldo Bastian"), KLocalizedString(),
"bastian@kde.org");
980 KCmdLineArgs::init(argc, argv, &aboutData);
981 KCmdLineArgs::addCmdLineOptions(options);
983 KComponentData componentData(&aboutData);
985 KonfUpdate konfUpdate;
QString toString(Qt::DateFormat format) const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
QStringList parseGroupString(const QString &_str, bool *ok, QString *error)
QTextStream & operator<<(QTextStream &stream, const QStringList &lst)
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
QString convertSeparators(const QString &pathName)
const_iterator constBegin() const
const T & at(int i) const
bool contains(const QString &str, Qt::CaseSensitivity cs) const
QString join(const QString &separator) const
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
int count(const T &value) const
void append(const T &value)
const_iterator constEnd() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
int kdemain(int argc, char **argv)
QDateTime currentDateTime()
const ushort * utf16() const
QString mid(int position, int n) const
QTextCodec * codecForName(const QByteArray &name)
KConfigGroup openGroup(KConfig *config, const QStringList &_lst)
QString left(int n) const
const_iterator constEnd() const
const_iterator constBegin() const
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QByteArray encodeName(const QString &fileName)