1/*
2 SPDX-FileCopyrightText: 2010-2018 Dominik Haumann <dhaumann@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6#include "kateswapdiffcreator.h"
7#include "katedocument.h"
8#include "katepartdebug.h"
9#include "kateswapfile.h"
10#include <ktexteditor/view.h>
11
12#include <KIO/JobUiDelegateFactory>
13#include <KIO/OpenUrlJob>
14#include <KLocalizedString>
15#include <KMessageBox>
16
17#include <QDir>
18#include <QStandardPaths>
19
20// BEGIN SwapDiffCreator
21SwapDiffCreator::SwapDiffCreator(Kate::SwapFile *swapFile)
22 : QObject(swapFile)
23 , m_swapFile(swapFile)
24{
25}
26
27void SwapDiffCreator::viewDiff()
28{
29 QString path = m_swapFile->fileName();
30 if (path.isNull()) {
31 return;
32 }
33
34 QFile swp(path);
35 if (!swp.open(flags: QIODevice::ReadOnly)) {
36 qCWarning(LOG_KTE) << "Can't open swap file";
37 return;
38 }
39
40 // create all needed tempfiles
41 m_originalFile.setFileTemplate(QDir::temp().filePath(QStringLiteral("katepart_XXXXXX.original")));
42 m_recoveredFile.setFileTemplate(QDir::temp().filePath(QStringLiteral("katepart_XXXXXX.recovered")));
43 m_diffFile.setFileTemplate(QDir::temp().filePath(QStringLiteral("katepart_XXXXXX.diff")));
44
45 if (!m_originalFile.open() || !m_recoveredFile.open() || !m_diffFile.open()) {
46 qCWarning(LOG_KTE) << "Can't open temporary files needed for diffing";
47 return;
48 }
49
50 // truncate files, just in case
51 m_originalFile.resize(sz: 0);
52 m_recoveredFile.resize(sz: 0);
53 m_diffFile.resize(sz: 0);
54
55 // create a document with the recovered data
56 KTextEditor::DocumentPrivate recoverDoc;
57 recoverDoc.setText(m_swapFile->document()->text());
58
59 // store original text in a file as utf-8 and close it
60 {
61 QTextStream stream(&m_originalFile);
62 stream << recoverDoc.text();
63 }
64 m_originalFile.close();
65
66 // recover data
67 QDataStream stream(&swp);
68 recoverDoc.swapFile()->recover(stream, checkDigest: false);
69
70 // store recovered text in a file as utf-8 and close it
71 {
72 QTextStream stream(&m_recoveredFile);
73 stream << recoverDoc.text();
74 }
75 m_recoveredFile.close();
76
77 // create a process for diff
78 m_proc.setProcessChannelMode(QProcess::MergedChannels);
79
80 connect(sender: &m_proc, signal: &QProcess::readyRead, context: this, slot: &SwapDiffCreator::slotDataAvailable, type: Qt::UniqueConnection);
81 connect(sender: &m_proc, signal: &QProcess::finished, context: this, slot: &SwapDiffCreator::slotDiffFinished, type: Qt::UniqueConnection);
82
83 // use diff from PATH only => inform if not found at all
84 const QString fullDiffPath = QStandardPaths::findExecutable(QStringLiteral("diff"));
85 if (fullDiffPath.isEmpty()) {
86 KMessageBox::error(parent: m_swapFile->document()->activeView(),
87 i18n("The diff command could not be found. Please make sure that "
88 "diff(1) is installed and in your PATH."),
89 i18n("Error Creating Diff"));
90 deleteLater();
91 return;
92 }
93
94 // try to start the diff program, might fail, too
95 m_proc.start(program: fullDiffPath, arguments: QStringList() << QStringLiteral("-u") << m_originalFile.fileName() << m_recoveredFile.fileName());
96 if (!m_proc.waitForStarted()) {
97 KMessageBox::error(parent: m_swapFile->document()->activeView(),
98 i18n("The diff command '%1' could not be started.").arg(a: fullDiffPath),
99 i18n("Error Creating Diff"));
100 deleteLater();
101 return;
102 }
103
104 // process is up and running, we can write data to it
105 QTextStream ts(&m_proc);
106 int lineCount = recoverDoc.lines();
107 for (int line = 0; line < lineCount; ++line) {
108 ts << recoverDoc.line(line) << '\n';
109 }
110 ts.flush();
111 m_proc.closeWriteChannel();
112}
113
114void SwapDiffCreator::slotDataAvailable()
115{
116 // collect diff output
117 m_diffFile.write(data: m_proc.readAll());
118}
119
120void SwapDiffCreator::slotDiffFinished()
121{
122 // collect last diff output, if any
123 m_diffFile.write(data: m_proc.readAll());
124
125 // get the exit status to check whether diff command run successfully
126 const QProcess::ExitStatus es = m_proc.exitStatus();
127
128 // check exit status
129 if (es != QProcess::NormalExit) {
130 KMessageBox::error(parent: m_swapFile->document()->activeView(),
131 i18n("The diff command failed. Please make sure that "
132 "diff(1) is installed and in your PATH."),
133 i18n("Error Creating Diff"));
134 deleteLater();
135 return;
136 }
137
138 // sanity check: is there any diff content?
139 if (m_diffFile.size() == 0) {
140 KMessageBox::information(parent: m_swapFile->document()->activeView(), i18n("The files are identical."), i18n("Diff Output"));
141 deleteLater();
142 return;
143 }
144
145 // close diffFile and avoid removal, KIO::OpenUrlJob will do that later!
146 m_diffFile.close();
147 m_diffFile.setAutoRemove(false);
148
149 KIO::OpenUrlJob *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(localfile: m_diffFile.fileName()), QStringLiteral("text/x-patch"));
150 job->setUiDelegate(KIO::createDefaultJobUiDelegate(flags: KJobUiDelegate::AutoHandlingEnabled, window: m_swapFile->document()->activeView()));
151 job->setDeleteTemporaryFile(true); // delete the file, once the client exits
152 job->start();
153
154 deleteLater();
155}
156
157// END SwapDiffCreator
158

source code of ktexteditor/src/swapfile/kateswapdiffcreator.cpp