[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
klffilterprocess.cpp
1/***************************************************************************
2 * file klffilterprocess.cpp
3 * This file is part of the KLatexFormula Project.
4 * Copyright (C) 2011 by Philippe Faist
5 * philippe.faist at bluewin.ch
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the *
19 * Free Software Foundation, Inc., *
20 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
21 ***************************************************************************/
22/* $Id$ */
23
24#include <QString>
25#include <QFile>
26#include <QProcess>
27
28#include <klfdefs.h>
29
30#include "klfbackend.h"
31#include "klfblockprocess.h"
32#include "klffilterprocess.h"
33
34#include "klffilterprocess_p.h"
35
36// -----------------
37
38// Utility function
39static QString progErrorMsg(QString progname, int exitstatus, QString stderrstr, QString stdoutstr)
40{
41 QString stdouthtml = stdoutstr;
42 QString stderrhtml = stderrstr;
43 stdouthtml.replace("&", "&amp;");
44 stdouthtml.replace("<", "&lt;");
45 stdouthtml.replace(">", "&gt;");
46 stderrhtml.replace("&", "&amp;");
47 stderrhtml.replace("<", "&lt;");
48 stderrhtml.replace(">", "&gt;");
49
50 if (stderrstr.isEmpty() && stdoutstr.isEmpty())
51 return QObject::tr("<p><b>%1</b> reported an error (exit status %2). No Output was generated.</p>",
52 "KLFBackend")
53 .arg(progname).arg(exitstatus);
54 if (stderrstr.isEmpty())
55 return
56 QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stdout output:</p>\n"
57 "<pre>\n%3</pre>", "KLFBackend")
58 .arg(progname).arg(exitstatus).arg(stdouthtml);
59 if (stdoutstr.isEmpty())
60 return
61 QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
62 "<pre>\n%3</pre>", "KLFBackend")
63 .arg(progname).arg(exitstatus).arg(stderrhtml);
64
65 return QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
66 "<pre>\n%3</pre><p>And here is full stdout output:</p><pre>\n%4</pre>", "KLFBackend")
67 .arg(progname).arg(exitstatus).arg(stderrhtml).arg(stdouthtml);
68}
69
70
71
72
73
74
75// ------------------
76
77KLFFilterProcessBlockProcess::KLFFilterProcessBlockProcess(KLFFilterProcess * fproc)
78 : KLFBlockProcess(), pFproc(fproc)
79{
80}
81KLFFilterProcessBlockProcess::~KLFFilterProcessBlockProcess()
82{
83}
84QString KLFFilterProcessBlockProcess::getInterpreterPath(const QString& ext)
85{
86 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
87 klfDbg("ext = " << ext) ;
88 QMap<QString,QString> interpreters = pFproc->interpreters();
89 QMap<QString,QString>::Iterator it = interpreters.find(ext);
90 if (it != interpreters.end()) {
91 return *it;
92 }
94}
95
96
97// -----------------
98
99
100struct KLFFilterProcessPrivate {
102 {
103 }
104
105 void init(const QString& pTitle, const KLFBackend::klfSettings *settings,
106 const QString& rundir, bool inheritProcessEnvironment);
107
108 QString progTitle;
109 QString programCwd;
110 QStringList execEnviron;
111
112 QStringList argv;
113
114 QMap<QString,QString> interpreters;
115
116 bool outputStdout;
117 bool outputStderr;
118
119 QByteArray *collectStdout;
120 QByteArray *collectStderr;
121
122 bool processAppEvents;
123
124 // these fields are set after calling run()
125 int exitStatus;
126 int exitCode;
127
128 int res;
129 QString resErrorString;
130};
131
132// ---------
133
134
135KLFFilterProcess::KLFFilterProcess(const QString& pTitle, const KLFBackend::klfSettings *settings,
136 const QString& rundir)
137{
139 d->init(pTitle, settings, rundir, false);
140}
141
142KLFFilterProcess::KLFFilterProcess(const QString& pTitle, const KLFBackend::klfSettings *settings,
143 const QString& rundir, bool inheritProcessEnvironment)
144{
146 d->init(pTitle, settings, rundir, inheritProcessEnvironment);
147}
148
149void KLFFilterProcessPrivate::init(const QString& pTitle, const KLFBackend::klfSettings *settings,
150 const QString& rundir, bool inheritProcessEnvironment)
151{
152 progTitle = pTitle;
153
154 collectStdout = NULL;
155 collectStderr = NULL;
156
157 outputStdout = true;
158 outputStderr = false;
159
160 interpreters = QMap<QString,QString>();
161
162 if (rundir.size()) {
163 programCwd = rundir;
164 }
165 if (settings != NULL) {
166 if (!rundir.size()) {
167 programCwd = settings->tempdir;
168 klfDbg("set programCwd to : "<<programCwd) ;
169 }
170 QStringList curenv;
171 if (inheritProcessEnvironment) {
172 curenv = klfCurrentEnvironment();
173 }
174 execEnviron = klfMergeEnvironment(curenv, settings->execenv,
175 QStringList()<<"PATH"<<"TEXINPUTS"<<"BIBINPUTS"<<"PYTHONPATH",
176 KlfEnvPathPrepend|KlfEnvMergeExpandVars);
177 klfDbg("set execution environment to : "<<execEnviron) ;
178
179 interpreters = settings->userScriptInterpreters;
180 }
181
182 processAppEvents = true;
183
184 exitStatus = -1;
185 exitCode = -1;
186 res = -1;
187 resErrorString = QString();
188}
189
190
191KLFFilterProcess::~KLFFilterProcess()
192{
194}
195
196
197
198QString KLFFilterProcess::progTitle() const
199{
200 return d->progTitle;
201}
202void KLFFilterProcess::setProgTitle(const QString& title)
203{
204 d->progTitle = title;
205}
206
207QString KLFFilterProcess::programCwd() const
208{
209 return d->programCwd;
210}
211void KLFFilterProcess::setProgramCwd(const QString& cwd)
212{
213 d->programCwd = cwd;
214}
215
216QStringList KLFFilterProcess::execEnviron() const
217{
218 return d->execEnviron;
219}
220void KLFFilterProcess::setExecEnviron(const QStringList& env)
221{
222 d->execEnviron = env;
223 klfDbg("set exec environment: " << d->execEnviron);
224}
225void KLFFilterProcess::addExecEnviron(const QStringList& env)
226{
227 klfMergeEnvironment(& d->execEnviron, env);
228 klfDbg("merged exec environment: " << d->execEnviron);
229}
230
231QStringList KLFFilterProcess::argv() const
232{
233 return d->argv;
234}
235void KLFFilterProcess::setArgv(const QStringList& argv)
236{
237 d->argv = argv;
238}
239void KLFFilterProcess::addArgv(const QStringList& argv)
240{
241 d->argv << argv;
242}
243void KLFFilterProcess::addArgv(const QString& argv)
244{
245 d->argv << argv;
246}
247
248bool KLFFilterProcess::outputStdout() const
249{
250 return d->outputStdout;
251}
253{
254 d->outputStdout = on;
255}
256
257bool KLFFilterProcess::outputStderr() const
258{
259 return d->outputStderr;
260}
262{
263 d->outputStderr = on;
264}
265
267{
268 setOutputStdout(true);
269 d->collectStdout = stdoutstore;
270}
272{
273 setOutputStderr(true);
274 d->collectStderr = stderrstore;
275}
276
278{
279 return d->processAppEvents;
280}
281
283{
284 d->processAppEvents = on;
285}
286
288{
289 return d->exitStatus;
290}
292{
293 return d->exitCode;
294}
295
297{
298 return d->res;
299}
301{
302 return d->resErrorString;
303}
304
305QMap<QString,QString> KLFFilterProcess::interpreters() const
306{
307 return d->interpreters;
308}
309
311{
313
314 KLFFilterProcessBlockProcess proc(this);
315
316 d->exitCode = 0;
317 d->exitStatus = 0;
318
319 KLF_ASSERT_CONDITION(d->argv.size() > 0, "argv array is empty! No program is given!", return false; ) ;
320
321 proc.setWorkingDirectory(d->programCwd);
322
323 proc.setProcessAppEvents(d->processAppEvents);
324
325 klfDbg("about to exec "<<d->progTitle<<" ...") ;
326 klfDbg("\t"<<qPrintable(d->argv.join(" "))) ;
327 bool r = proc.startProcess(d->argv, indata, d->execEnviron);
328 klfDbg(d->progTitle<<" returned.") ;
329
330 if (!r) {
331 klfDbg("couldn't launch " << d->progTitle) ;
332 d->res = KLFFP_NOSTART;
333 d->resErrorString = QObject::tr("Unable to start %1 program `%2'!", "KLFBackend").arg(d->progTitle, d->argv[0]);
334 return false;
335 }
336 if (!proc.processNormalExit()) {
337 klfDbg(d->progTitle<<" did not exit normally (crashed)") ;
338 d->exitStatus = proc.exitStatus();
339 d->exitCode = -1;
340 d->res = KLFFP_NOEXIT;
341 d->resErrorString = QObject::tr("Program %1 crashed!", "KLFBackend").arg(d->progTitle);
342 return false;
343 }
344 if (proc.processExitStatus() != 0) {
345 d->exitStatus = 0;
346 d->exitCode = proc.processExitStatus();
347 klfDbg(d->progTitle<<" exited with code "<<d->exitCode) ;
348 d->res = KLFFP_NOSUCCESSEXIT;
349 d->resErrorString = progErrorMsg(d->progTitle, proc.processExitStatus(), proc.readStderrString(),
350 proc.readStdoutString());
351 return false;
352 }
353
354 if (d->collectStdout != NULL) {
355 *d->collectStdout = proc.getAllStdout();
356 }
357 if (d->collectStderr != NULL) {
358 *d->collectStderr = proc.getAllStderr();
359 }
360
361 for (QMap<QString,QByteArray*>::const_iterator it = outdatalist.begin(); it != outdatalist.end(); ++it) {
362 QString outFileName = it.key();
363 QByteArray * outdata = it.value();
364
365 KLF_ASSERT_NOT_NULL(outdata, "Given NULL outdata pointer for file "<<outFileName<<" !", return false; ) ;
366
367 klfDbg("Will collect output in file "<<(outFileName.isEmpty()?QString("(stdout)"):outFileName)
368 <<" to its corresponding QByteArray pointer="<<outdata) ;
369
370 if (outFileName.isEmpty()) {
371 // empty outFileName means to use standard output
372 *outdata = QByteArray();
373 if (d->outputStdout) {
374 QByteArray stdoutdata = (d->collectStdout != NULL) ? *d->collectStdout : proc.getAllStdout();
375 *outdata += stdoutdata;
376 }
377 if (d->outputStderr) {
378 QByteArray stderrdata = (d->collectStderr != NULL) ? *d->collectStderr : proc.getAllStderr();
379 *outdata += stderrdata;
380 }
381 if (outdata->isEmpty()) {
382 // no data
383 QString stderrstr = (!d->outputStderr) ? ("\n"+proc.readStderrString()) : QString();
384 klfDbg(d->progTitle<<" did not provide any data. Error message: "<<stderrstr);
385 d->res = KLFFP_NODATA;
386 d->resErrorString = QObject::tr("Program %1 did not provide any output data.", "KLFBackend")
387 .arg(d->progTitle) + stderrstr;
388 return false;
389 }
390 // read standard output to buffer, continue with other possible outputs
391 continue;
392 }
393
394 if (!QFile::exists(outFileName)) {
395 klfDbg("File "<<outFileName<<" did not appear after running "<<d->progTitle) ;
396 d->res = KLFFP_NODATA;
397 d->resErrorString = QObject::tr("Output file didn't appear after having called %1!", "KLFBackend")
398 .arg(d->progTitle);
399 return false;
400 }
401
402 // read output file into outdata
403 QFile outfile(outFileName);
404 r = outfile.open(QIODevice::ReadOnly);
405 if ( ! r ) {
406 klfDbg("File "<<outFileName<<" cannot be read (after running "<<d->progTitle<<")") ;
407 d->res = KLFFP_DATAREADFAIL;
408 d->resErrorString = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(outFileName);
409 return false;
410 }
411
412 *outdata = outfile.readAll();
413 klfDbg("Read file "<<outFileName<<", got data, length="<<outdata->size());
414 }
415
416 klfDbg(d->progTitle<<" was successfully run and output successfully retrieved.") ;
417
418 // all OK
419 d->exitStatus = 0;
420 d->exitCode = 0;
421 d->res = KLFFP_NOERR;
422 d->resErrorString = QString();
423
424 return true;
425}
426
427
429{
430 if (!d->outputStdout || d->collectStdout == NULL) {
431 return QByteArray();
432 }
433 return *d->collectStdout;
434}
436{
437 if (!d->outputStderr || d->collectStderr == NULL) {
438 return QByteArray();
439 }
440 return *d->collectStderr;
441}
A QProcess subclass for code-blocking process execution.
virtual QString getInterpreterPath(const QString &ext)
The interpter path to use for the given extension.
virtual int exitCode() const
void collectStderrTo(QByteArray *stderrstore)
virtual bool do_run(const QByteArray &indata, const QMap< QString, QByteArray * > outdatalist)
Actually run the process.
QByteArray collectedStdout() const
The collected stdout data of the process that just ran.
void setOutputStderr(bool on)
QByteArray collectedStderr() const
The collected stderr data of the process that just ran.
virtual QString resultErrorString() const
virtual int resultStatus() const
void collectStdoutTo(QByteArray *stdoutstore)
virtual int exitStatus() const
void setProcessAppEvents(bool processEvents)
void setOutputStdout(bool on)
Definition of class KLFBackend.
Defines the KLFBlockProcess class.
#define KLF_DEBUG_BLOCK(msg)
#define KLF_ASSERT_NOT_NULL(ptr, msg, failaction)
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
#define KLF_FUNC_NAME
#define klfDbg(streamableItems)
#define KLF_PRIVATE_HEAD(ClassName)
#define KLF_DELETE_PRIVATE
#define KLF_INIT_PRIVATE(ClassName)
void klfMergeEnvironment(QStringList *env, const QStringList &addvars, const QStringList &pathvars, uint actions)
KLF_EXPORT QStringList klfCurrentEnvironment()
bool isEmpty() const
int size() const
bool exists() const
virtual bool open(OpenMode mode)
QByteArray readAll()
iterator begin()
iterator end()
iterator find(const Key &key)
const Key key(const T &value, const Key &defaultKey) const
const T value(const Key &key, const T &defaultValue) const
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
bool isEmpty() const
QString & replace(int position, int n, QChar after)
int size() const
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:219
QMap< QString, QString > userScriptInterpreters
Definition: klfbackend.h:300

Generated by doxygen 1.9.6