File indexing completed on 2024-04-14 03:55:24

0001 /*
0002     SPDX-FileCopyrightText: 2010-2018 Dominik Haumann <dhaumann@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 #include "kateswapdiffcreator.h"
0007 #include "katedocument.h"
0008 #include "katepartdebug.h"
0009 #include "kateswapfile.h"
0010 #include <ktexteditor/view.h>
0011 
0012 #include <KIO/JobUiDelegateFactory>
0013 #include <KIO/OpenUrlJob>
0014 #include <KLocalizedString>
0015 #include <KMessageBox>
0016 
0017 #include <QDir>
0018 #include <QStandardPaths>
0019 
0020 // BEGIN SwapDiffCreator
0021 SwapDiffCreator::SwapDiffCreator(Kate::SwapFile *swapFile)
0022     : QObject(swapFile)
0023     , m_swapFile(swapFile)
0024 {
0025 }
0026 
0027 void SwapDiffCreator::viewDiff()
0028 {
0029     QString path = m_swapFile->fileName();
0030     if (path.isNull()) {
0031         return;
0032     }
0033 
0034     QFile swp(path);
0035     if (!swp.open(QIODevice::ReadOnly)) {
0036         qCWarning(LOG_KTE) << "Can't open swap file";
0037         return;
0038     }
0039 
0040     // create all needed tempfiles
0041     m_originalFile.setFileTemplate(QDir::temp().filePath(QStringLiteral("katepart_XXXXXX.original")));
0042     m_recoveredFile.setFileTemplate(QDir::temp().filePath(QStringLiteral("katepart_XXXXXX.recovered")));
0043     m_diffFile.setFileTemplate(QDir::temp().filePath(QStringLiteral("katepart_XXXXXX.diff")));
0044 
0045     if (!m_originalFile.open() || !m_recoveredFile.open() || !m_diffFile.open()) {
0046         qCWarning(LOG_KTE) << "Can't open temporary files needed for diffing";
0047         return;
0048     }
0049 
0050     // truncate files, just in case
0051     m_originalFile.resize(0);
0052     m_recoveredFile.resize(0);
0053     m_diffFile.resize(0);
0054 
0055     // create a document with the recovered data
0056     KTextEditor::DocumentPrivate recoverDoc;
0057     recoverDoc.setText(m_swapFile->document()->text());
0058 
0059     // store original text in a file as utf-8 and close it
0060     {
0061         QTextStream stream(&m_originalFile);
0062         stream << recoverDoc.text();
0063     }
0064     m_originalFile.close();
0065 
0066     // recover data
0067     QDataStream stream(&swp);
0068     recoverDoc.swapFile()->recover(stream, false);
0069 
0070     // store recovered text in a file as utf-8 and close it
0071     {
0072         QTextStream stream(&m_recoveredFile);
0073         stream << recoverDoc.text();
0074     }
0075     m_recoveredFile.close();
0076 
0077     // create a process for diff
0078     m_proc.setProcessChannelMode(QProcess::MergedChannels);
0079 
0080     connect(&m_proc, &QProcess::readyRead, this, &SwapDiffCreator::slotDataAvailable, Qt::UniqueConnection);
0081     connect(&m_proc, &QProcess::finished, this, &SwapDiffCreator::slotDiffFinished, Qt::UniqueConnection);
0082 
0083     // use diff from PATH only => inform if not found at all
0084     const QString fullDiffPath = QStandardPaths::findExecutable(QStringLiteral("diff"));
0085     if (fullDiffPath.isEmpty()) {
0086         KMessageBox::error(m_swapFile->document()->activeView(),
0087                            i18n("The diff command could not be found. Please make sure that "
0088                                 "diff(1) is installed and in your PATH."),
0089                            i18n("Error Creating Diff"));
0090         deleteLater();
0091         return;
0092     }
0093 
0094     // try to start the diff program, might fail, too
0095     m_proc.start(fullDiffPath, QStringList() << QStringLiteral("-u") << m_originalFile.fileName() << m_recoveredFile.fileName());
0096     if (!m_proc.waitForStarted()) {
0097         KMessageBox::error(m_swapFile->document()->activeView(),
0098                            i18n("The diff command '%1' could not be started.").arg(fullDiffPath),
0099                            i18n("Error Creating Diff"));
0100         deleteLater();
0101         return;
0102     }
0103 
0104     // process is up and running, we can write data to it
0105     QTextStream ts(&m_proc);
0106     int lineCount = recoverDoc.lines();
0107     for (int line = 0; line < lineCount; ++line) {
0108         ts << recoverDoc.line(line) << '\n';
0109     }
0110     ts.flush();
0111     m_proc.closeWriteChannel();
0112 }
0113 
0114 void SwapDiffCreator::slotDataAvailable()
0115 {
0116     // collect diff output
0117     m_diffFile.write(m_proc.readAll());
0118 }
0119 
0120 void SwapDiffCreator::slotDiffFinished()
0121 {
0122     // collect last diff output, if any
0123     m_diffFile.write(m_proc.readAll());
0124 
0125     // get the exit status to check whether diff command run successfully
0126     const QProcess::ExitStatus es = m_proc.exitStatus();
0127 
0128     // check exit status
0129     if (es != QProcess::NormalExit) {
0130         KMessageBox::error(m_swapFile->document()->activeView(),
0131                            i18n("The diff command failed. Please make sure that "
0132                                 "diff(1) is installed and in your PATH."),
0133                            i18n("Error Creating Diff"));
0134         deleteLater();
0135         return;
0136     }
0137 
0138     // sanity check: is there any diff content?
0139     if (m_diffFile.size() == 0) {
0140         KMessageBox::information(m_swapFile->document()->activeView(), i18n("The files are identical."), i18n("Diff Output"));
0141         deleteLater();
0142         return;
0143     }
0144 
0145     // close diffFile and avoid removal, KIO::OpenUrlJob will do that later!
0146     m_diffFile.close();
0147     m_diffFile.setAutoRemove(false);
0148 
0149     KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(m_diffFile.fileName()), QStringLiteral("text/x-patch"));
0150     job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, m_swapFile->document()->activeView()));
0151     job->setDeleteTemporaryFile(true); // delete the file, once the client exits
0152     job->start();
0153 
0154     deleteLater();
0155 }
0156 
0157 // END SwapDiffCreator