1 /*
2 SPDX-FileCopyrightText: 2001-2005,2009 Otto Bruggeman <bruggie@gmail.com>
3 SPDX-FileCopyrightText: 2001-2003 John Firebaugh <jfirebaugh@kde.org>
4 SPDX-FileCopyrightText: 2007-2008 Kevin Kofler <kevin.kofler@chello.at>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "kompareprocess.h"
10
11 #include <QDir>
12 #include <QStringList>
13 #include <QTextCodec>
14
15 #include <KCharsets>
16 #include <KIO/Global>
17
18 #include <komparediffdebug.h>
19 #include "diffsettings.h"
20
21 namespace {
22 /// TODO: This should be replaced to QDir::relativeFilePath
constructRelativePath(const QString & from,const QString & to)23 static QString constructRelativePath(const QString& from, const QString& to)
24 {
25 QUrl fromURL(from);
26 QUrl toURL(to);
27 QUrl root;
28 int upLevels = 0;
29
30 // Find a common root.
31 root = fromURL;
32 while (root.isValid() && !root.isParentOf(toURL)) {
33 root = KIO::upUrl(root);
34 ++upLevels;
35 }
36
37 if (!root.isValid()) return to;
38
39 QString relative;
40 for (; upLevels > 0; --upLevels) {
41 relative += QStringLiteral("../");
42 }
43
44 relative += QString(to).remove(0, root.path().length());
45 return relative;
46 }
47 }
48
KompareProcess(DiffSettings * diffSettings,Kompare::DiffMode diffMode,const QString & source,const QString & destination,const QString & dir,Kompare::Mode mode)49 KompareProcess::KompareProcess(DiffSettings* diffSettings, Kompare::DiffMode diffMode, const QString& source, const QString& destination, const QString& dir, Kompare::Mode mode)
50 : KProcess(),
51 m_diffSettings(diffSettings),
52 m_mode(diffMode),
53 m_customString(nullptr),
54 m_textDecoder(nullptr)
55 {
56 // connect the signal that indicates that the process has exited
57 connect(this, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
58 this, &KompareProcess::slotFinished);
59
60 setEnv(QStringLiteral("LANG"), QStringLiteral("C"));
61
62 // Write command and options
63 if (m_mode == Kompare::Default)
64 {
65 writeDefaultCommandLine();
66 }
67 else
68 {
69 writeCommandLine();
70 }
71
72 if (!dir.isEmpty()) {
73 setWorkingDirectory(dir);
74 }
75
76 // Write file names
77 *this << QStringLiteral("--");
78
79 //Add the option for diff to read from stdin(QIODevice::write), and save a pointer to the string
80 if (mode == Kompare::ComparingStringFile)
81 {
82 *this << QStringLiteral("-");
83 m_customString = &source;
84 }
85 else
86 {
87 *this << constructRelativePath(dir, source);
88 }
89
90 if (mode == Kompare::ComparingFileString)
91 {
92 *this << QStringLiteral("-");
93 m_customString = &destination;
94 }
95 else
96 {
97 *this << constructRelativePath(dir, destination);
98 }
99 }
100
writeDefaultCommandLine()101 void KompareProcess::writeDefaultCommandLine()
102 {
103 if (!m_diffSettings || m_diffSettings->m_diffProgram.isEmpty())
104 {
105 *this << QStringLiteral("diff") << QStringLiteral("-dr");
106 }
107 else
108 {
109 *this << m_diffSettings->m_diffProgram << QStringLiteral("-dr");
110 }
111
112 *this << QStringLiteral("-U") << QString::number(m_diffSettings->m_linesOfContext);
113 }
114
writeCommandLine()115 void KompareProcess::writeCommandLine()
116 {
117 // load the executable into the KProcess
118 if (m_diffSettings->m_diffProgram.isEmpty())
119 {
120 qCDebug(LIBKOMPAREDIFF2) << "Using the first diff in the path...";
121 *this << QStringLiteral("diff");
122 }
123 else
124 {
125 qCDebug(LIBKOMPAREDIFF2) << "Using a user specified diff, namely: " << m_diffSettings->m_diffProgram;
126 *this << m_diffSettings->m_diffProgram;
127 }
128
129 switch (m_diffSettings->m_format) {
130 case Kompare::Unified :
131 *this << QStringLiteral("-U") << QString::number(m_diffSettings->m_linesOfContext);
132 break;
133 case Kompare::Context :
134 *this << QStringLiteral("-C") << QString::number(m_diffSettings->m_linesOfContext);
135 break;
136 case Kompare::RCS :
137 *this << QStringLiteral("-n");
138 break;
139 case Kompare::Ed :
140 *this << QStringLiteral("-e");
141 break;
142 case Kompare::SideBySide:
143 *this << QStringLiteral("-y");
144 break;
145 case Kompare::Normal :
146 case Kompare::UnknownFormat :
147 default:
148 break;
149 }
150
151 if (m_diffSettings->m_largeFiles
152 // default diff does not have -H on OpenBSD
153 // so don't pass this option unless the user overrode the default program
154 #if defined(__OpenBSD__)
155 && !m_diffSettings->m_diffProgram.isEmpty()
156 #endif
157 )
158 {
159 *this << QStringLiteral("-H");
160 }
161
162 if (m_diffSettings->m_ignoreWhiteSpace)
163 {
164 *this << QStringLiteral("-b");
165 }
166
167 if (m_diffSettings->m_ignoreAllWhiteSpace)
168 {
169 *this << QStringLiteral("-w");
170 }
171
172 if (m_diffSettings->m_ignoreEmptyLines)
173 {
174 *this << QStringLiteral("-B");
175 }
176
177 if (m_diffSettings->m_ignoreChangesDueToTabExpansion)
178 {
179 *this << QStringLiteral("-E");
180 }
181
182 if (m_diffSettings->m_createSmallerDiff)
183 {
184 *this << QStringLiteral("-d");
185 }
186
187 if (m_diffSettings->m_ignoreChangesInCase)
188 {
189 *this << QStringLiteral("-i");
190 }
191
192 if (m_diffSettings->m_ignoreRegExp && !m_diffSettings->m_ignoreRegExpText.isEmpty())
193 {
194 *this << QStringLiteral("-I") << m_diffSettings->m_ignoreRegExpText;
195 }
196
197 if (m_diffSettings->m_showCFunctionChange)
198 {
199 *this << QStringLiteral("-p");
200 }
201
202 if (m_diffSettings->m_convertTabsToSpaces)
203 {
204 *this << QStringLiteral("-t");
205 }
206
207 if (m_diffSettings->m_recursive)
208 {
209 *this << QStringLiteral("-r");
210 }
211
212 if (m_diffSettings->m_newFiles)
213 {
214 *this << QStringLiteral("-N");
215 }
216
217 // This option is more trouble than it is worth... please do not ever enable it unless you want really weird crashes
218 // if ( m_diffSettings->m_allText )
219 // {
220 // *this << QStringLiteral("-a");
221 // }
222
223 if (m_diffSettings->m_excludeFilePattern)
224 {
225 for (const QString& it : qAsConst(m_diffSettings->m_excludeFilePatternList)) {
226 *this << QStringLiteral("-x") << it;
227 }
228 }
229
230 if (m_diffSettings->m_excludeFilesFile && !m_diffSettings->m_excludeFilesFileURL.isEmpty())
231 {
232 *this << QStringLiteral("-X") << m_diffSettings->m_excludeFilesFileURL;
233 }
234 }
235
~KompareProcess()236 KompareProcess::~KompareProcess()
237 {
238 delete m_textDecoder;
239 }
240
setEncoding(const QString & encoding)241 void KompareProcess::setEncoding(const QString& encoding)
242 {
243 if (!encoding.compare(QLatin1String("default"), Qt::CaseInsensitive))
244 {
245 m_textDecoder = QTextCodec::codecForLocale()->makeDecoder();
246 }
247 else
248 {
249 m_codec = KCharsets::charsets()->codecForName(encoding);
250 if (m_codec)
251 m_textDecoder = m_codec->makeDecoder();
252 else
253 {
254 qCDebug(LIBKOMPAREDIFF2) << "Using locale codec as backup...";
255 m_codec = QTextCodec::codecForLocale();
256 m_textDecoder = m_codec->makeDecoder();
257 }
258 }
259 }
260
start()261 void KompareProcess::start()
262 {
263 #ifndef NDEBUG
264 QString cmdLine;
265 QStringList program = KProcess::program();
266 QStringList::ConstIterator it = program.constBegin();
267 QStringList::ConstIterator end = program.constEnd();
268 for (; it != end; ++it)
269 cmdLine += QLatin1Char('\"') + (*it) + QLatin1String("\" ");
270 qCDebug(LIBKOMPAREDIFF2) << cmdLine;
271 #endif
272 setOutputChannelMode(SeparateChannels);
273 setNextOpenMode(QIODevice::ReadWrite);
274 KProcess::start();
275
276 //If we have a string to compare against input it now
277 if (m_customString)
278 write(m_codec->fromUnicode(*m_customString));
279 closeWriteChannel();
280 }
281
slotFinished(int exitCode,QProcess::ExitStatus exitStatus)282 void KompareProcess::slotFinished(int exitCode, QProcess::ExitStatus exitStatus)
283 {
284 // add all output to m_stdout/m_stderr
285 if (m_textDecoder)
286 {
287 m_stdout = m_textDecoder->toUnicode(readAllStandardOutput());
288 m_stderr = m_textDecoder->toUnicode(readAllStandardError());
289 }
290 else
291 qCDebug(LIBKOMPAREDIFF2) << "KompareProcess::slotFinished : No decoder !!!";
292
293 // exit code of 0: no differences
294 // 1: some differences
295 // 2: error but there may be differences !
296 qCDebug(LIBKOMPAREDIFF2) << "Exited with exit code : " << exitCode;
297 Q_EMIT diffHasFinished(exitStatus == NormalExit && exitCode != 0);
298 }
299
300
301