[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: klffilterprocess.cpp 998 2017-01-19 23:54:56Z phfaist $ */
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
39 static 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 
77 KLFFilterProcessBlockProcess::KLFFilterProcessBlockProcess(KLFFilterProcess * fproc)
78  : KLFBlockProcess(), pFproc(fproc)
79 {
80 }
81 KLFFilterProcessBlockProcess::~KLFFilterProcessBlockProcess()
82 {
83 }
84 QString 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 
100 struct KLFFilterProcessPrivate {
102  {
103  }
104 
105  QString progTitle;
106  QString programCwd;
107  QStringList execEnviron;
108 
109  QStringList argv;
110 
111  QMap<QString,QString> interpreters;
112 
113  bool outputStdout;
114  bool outputStderr;
115 
116  QByteArray *collectStdout;
117  QByteArray *collectStderr;
118 
119  bool processAppEvents;
120 
121  // these fields are set after calling run()
122  int exitStatus;
123  int exitCode;
124 
125  int res;
126  QString resErrorString;
127 };
128 
129 // ---------
130 
131 
132 KLFFilterProcess::KLFFilterProcess(const QString& pTitle, const KLFBackend::klfSettings *settings,
133  const QString& rundir)
134 {
136 
137  d->progTitle = pTitle;
138 
139  d->collectStdout = NULL;
140  d->collectStderr = NULL;
141 
142  d->outputStdout = true;
143  d->outputStderr = false;
144 
145  d->interpreters = QMap<QString,QString>();
146 
147  if (rundir.size()) {
148  d->programCwd = rundir;
149  }
150  if (settings != NULL) {
151  if (!rundir.size()) {
152  d->programCwd = settings->tempdir;
153  klfDbg("set programCwd to : "<<d->programCwd) ;
154  }
155  d->execEnviron = klfMergeEnvironment(QStringList(), settings->execenv, QStringList(),
156  KlfEnvPathPrepend|KlfEnvMergeExpandVars);
157  klfDbg("set execution environment to : "<<d->execEnviron) ;
158 
159  d->interpreters = settings->userScriptInterpreters;
160  }
161 
162  d->processAppEvents = true;
163 
164  d->exitStatus = -1;
165  d->exitCode = -1;
166  d->res = -1;
167  d->resErrorString = QString();
168 }
169 
170 KLFFilterProcess::~KLFFilterProcess()
171 {
173 }
174 
175 
176 
177 QString KLFFilterProcess::progTitle() const
178 {
179  return d->progTitle;
180 }
181 void KLFFilterProcess::setProgTitle(const QString& title)
182 {
183  d->progTitle = title;
184 }
185 
186 QString KLFFilterProcess::programCwd() const
187 {
188  return d->programCwd;
189 }
190 void KLFFilterProcess::setProgramCwd(const QString& cwd)
191 {
192  d->programCwd = cwd;
193 }
194 
195 QStringList KLFFilterProcess::execEnviron() const
196 {
197  return d->execEnviron;
198 }
199 void KLFFilterProcess::setExecEnviron(const QStringList& env)
200 {
201  d->execEnviron = env;
202  klfDbg("set exec environment: " << d->execEnviron);
203 }
204 void KLFFilterProcess::addExecEnviron(const QStringList& env)
205 {
206  klfMergeEnvironment(& d->execEnviron, env);
207  klfDbg("merged exec environment: " << d->execEnviron);
208 }
209 
210 QStringList KLFFilterProcess::argv() const
211 {
212  return d->argv;
213 }
214 void KLFFilterProcess::setArgv(const QStringList& argv)
215 {
216  d->argv = argv;
217 }
218 void KLFFilterProcess::addArgv(const QStringList& argv)
219 {
220  d->argv << argv;
221 }
222 void KLFFilterProcess::addArgv(const QString& argv)
223 {
224  d->argv << argv;
225 }
226 
227 bool KLFFilterProcess::outputStdout() const
228 {
229  return d->outputStdout;
230 }
232 {
233  d->outputStdout = on;
234 }
235 
236 bool KLFFilterProcess::outputStderr() const
237 {
238  return d->outputStderr;
239 }
241 {
242  d->outputStderr = on;
243 }
244 
246 {
247  setOutputStdout(true);
248  d->collectStdout = stdoutstore;
249 }
251 {
252  setOutputStderr(true);
253  d->collectStderr = stderrstore;
254 }
255 
257 {
258  return d->processAppEvents;
259 }
260 
262 {
263  d->processAppEvents = on;
264 }
265 
267 {
268  return d->exitStatus;
269 }
270 int KLFFilterProcess::exitCode() const
271 {
272  return d->exitCode;
273 }
274 
276 {
277  return d->res;
278 }
280 {
281  return d->resErrorString;
282 }
283 
284 QMap<QString,QString> KLFFilterProcess::interpreters() const
285 {
286  return d->interpreters;
287 }
288 
289 bool KLFFilterProcess::do_run(const QByteArray& indata, const QMap<QString, QByteArray*> outdatalist)
290 {
292 
293  KLFBlockProcess proc;
294 
295  d->exitCode = 0;
296  d->exitStatus = 0;
297 
298  KLF_ASSERT_CONDITION(d->argv.size() > 0, "argv array is empty! No program is given!", return false; ) ;
299 
300  proc.setWorkingDirectory(d->programCwd);
301 
302  proc.setProcessAppEvents(d->processAppEvents);
303 
304  klfDbg("about to exec "<<d->progTitle<<" ...") ;
305  klfDbg("\t"<<qPrintable(d->argv.join(" "))) ;
306  bool r = proc.startProcess(d->argv, indata, d->execEnviron);
307  klfDbg(d->progTitle<<" returned.") ;
308 
309  if (!r) {
310  klfDbg("couldn't launch " << d->progTitle) ;
311  d->res = KLFFP_NOSTART;
312  d->resErrorString = QObject::tr("Unable to start %1 program `%2'!", "KLFBackend").arg(d->progTitle, d->argv[0]);
313  return false;
314  }
315  if (!proc.processNormalExit()) {
316  klfDbg(d->progTitle<<" did not exit normally (crashed)") ;
317  d->exitStatus = proc.exitStatus();
318  d->exitCode = -1;
319  d->res = KLFFP_NOEXIT;
320  d->resErrorString = QObject::tr("Program %1 crashed!", "KLFBackend").arg(d->progTitle);
321  return false;
322  }
323  if (proc.processExitStatus() != 0) {
324  d->exitStatus = 0;
325  d->exitCode = proc.processExitStatus();
326  klfDbg(d->progTitle<<" exited with code "<<d->exitCode) ;
327  d->res = KLFFP_NOSUCCESSEXIT;
328  d->resErrorString = progErrorMsg(d->progTitle, proc.processExitStatus(), proc.readStderrString(),
329  proc.readStdoutString());
330  return false;
331  }
332 
333  if (d->collectStdout != NULL) {
334  *d->collectStdout = proc.getAllStdout();
335  }
336  if (d->collectStderr != NULL) {
337  *d->collectStderr = proc.getAllStderr();
338  }
339 
340  for (QMap<QString,QByteArray*>::const_iterator it = outdatalist.begin(); it != outdatalist.end(); ++it) {
341  QString outFileName = it.key();
342  QByteArray * outdata = it.value();
343 
344  KLF_ASSERT_NOT_NULL(outdata, "Given NULL outdata pointer for file "<<outFileName<<" !", return false; ) ;
345 
346  klfDbg("Will collect output in file "<<(outFileName.isEmpty()?QString("(stdout)"):outFileName)
347  <<" to its corresponding QByteArray pointer="<<outdata) ;
348 
349  if (outFileName.isEmpty()) {
350  // empty outFileName means to use standard output
351  *outdata = QByteArray();
352  if (d->outputStdout) {
353  QByteArray stdoutdata = (d->collectStdout != NULL) ? *d->collectStdout : proc.getAllStdout();
354  *outdata += stdoutdata;
355  }
356  if (d->outputStderr) {
357  QByteArray stderrdata = (d->collectStderr != NULL) ? *d->collectStderr : proc.getAllStderr();
358  *outdata += stderrdata;
359  }
360  if (outdata->isEmpty()) {
361  // no data
362  QString stderrstr = (!d->outputStderr) ? ("\n"+proc.readStderrString()) : QString();
363  klfDbg(d->progTitle<<" did not provide any data. Error message: "<<stderrstr);
364  d->res = KLFFP_NODATA;
365  d->resErrorString = QObject::tr("Program %1 did not provide any output data.", "KLFBackend")
366  .arg(d->progTitle) + stderrstr;
367  return false;
368  }
369  // read standard output to buffer, continue with other possible outputs
370  continue;
371  }
372 
373  if (!QFile::exists(outFileName)) {
374  klfDbg("File "<<outFileName<<" did not appear after running "<<d->progTitle) ;
375  d->res = KLFFP_NODATA;
376  d->resErrorString = QObject::tr("Output file didn't appear after having called %1!", "KLFBackend")
377  .arg(d->progTitle);
378  return false;
379  }
380 
381  // read output file into outdata
382  QFile outfile(outFileName);
383  r = outfile.open(QIODevice::ReadOnly);
384  if ( ! r ) {
385  klfDbg("File "<<outFileName<<" cannot be read (after running "<<d->progTitle<<")") ;
386  d->res = KLFFP_DATAREADFAIL;
387  d->resErrorString = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(outFileName);
388  return false;
389  }
390 
391  *outdata = outfile.readAll();
392  klfDbg("Read file "<<outFileName<<", got data, length="<<outdata->size());
393  }
394 
395  klfDbg(d->progTitle<<" was successfully run and output successfully retrieved.") ;
396 
397  // all OK
398  d->exitStatus = 0;
399  d->exitCode = 0;
400  d->res = KLFFP_NOERR;
401  d->resErrorString = QString();
402 
403  return true;
404 }
405 
406 
408 {
409  if (!d->outputStdout || d->collectStdout == NULL) {
410  return QByteArray();
411  }
412  return *d->collectStdout;
413 }
415 {
416  if (!d->outputStderr || d->collectStderr == NULL) {
417  return QByteArray();
418  }
419  return *d->collectStderr;
420 }
KLFBackend::klfSettings::userScriptInterpreters
QMap< QString, QString > userScriptInterpreters
Definition: klfbackend.h:299
KLFFilterProcess::collectStderrTo
void collectStderrTo(QByteArray *stderrstore)
Definition: klffilterprocess.cpp:249
QString::size
int size() const
KLFBlockProcess::getAllStdout
QByteArray getAllStdout()
Definition: klfblockprocess.h:77
KLFBlockProcess::getInterpreterPath
virtual QString getInterpreterPath(const QString &ext)
The interpter path to use for the given extension.
Definition: klfblockprocess.cpp:117
QProcess::setWorkingDirectory
void setWorkingDirectory(const QString &dir)
KLFFilterProcess::resultErrorString
virtual QString resultErrorString() const
Definition: klffilterprocess.cpp:278
QFile::open
virtual bool open(OpenMode mode)
QMap::value
const T value(const Key &key, const T &defaultValue) const
QMap::begin
iterator begin()
KLFFilterProcess::exitStatus
virtual int exitStatus() const
Definition: klffilterprocess.cpp:265
KLFFilterProcess::setProcessAppEvents
void setProcessAppEvents(bool processEvents)
Definition: klffilterprocess.cpp:260
KLF_PRIVATE_HEAD
#define KLF_PRIVATE_HEAD(ClassName)
KLF_FUNC_NAME
#define KLF_FUNC_NAME
QFile::exists
bool exists() const
KLFBlockProcess::processNormalExit
bool processNormalExit() const
Definition: klfblockprocess.h:82
QMap::end
iterator end()
klfDbg
#define klfDbg(streamableItems)
QMap::find
iterator find(const Key &key)
KLF_DEBUG_BLOCK
#define KLF_DEBUG_BLOCK(msg)
QString
QProcess::exitStatus
QProcess::ExitStatus exitStatus() const
QString::isEmpty
bool isEmpty() const
KLF_DELETE_PRIVATE
#define KLF_DELETE_PRIVATE
KLFFilterProcess::processAppEvents
bool processAppEvents()
Definition: klffilterprocess.cpp:255
KLFFilterProcess
Definition: klffilterprocess.h:46
QMap::key
const Key key(const T &value, const Key &defaultKey) const
KLFBlockProcess
A QProcess subclass for code-blocking process execution.
Definition: klfblockprocess.h:55
QString::replace
QString & replace(int position, int n, QChar after)
KLFBlockProcess::getAllStderr
QByteArray getAllStderr()
Definition: klfblockprocess.h:71
KLFFilterProcess::collectStdoutTo
void collectStdoutTo(QByteArray *stdoutstore)
Definition: klffilterprocess.cpp:244
KLFBlockProcess::readStderrString
QString readStderrString()
Definition: klfblockprocess.h:127
klfbackend.h
Definition of class KLFBackend.
QMap< QString, QString >
klfblockprocess.h
Defines the KLFBlockProcess class.
QByteArray::isEmpty
bool isEmpty() const
KLF_ASSERT_CONDITION
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
KLFBackend::klfSettings::tempdir
QString tempdir
Definition: klfbackend.h:226
QMap::const_iterator
KLFFilterProcess::collectedStdout
QByteArray collectedStdout() const
The collected stdout data of the process that just ran.
Definition: klffilterprocess.cpp:406
KLFBackend::klfSettings
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:217
KLFBlockProcess::readStdoutString
QString readStdoutString()
Definition: klfblockprocess.h:131
QByteArray::size
int size() const
KLFFilterProcess::do_run
virtual bool do_run(const QByteArray &indata, const QMap< QString, QByteArray * > outdatalist)
Actually run the process.
Definition: klffilterprocess.cpp:288
KLF_INIT_PRIVATE
#define KLF_INIT_PRIVATE(ClassName)
klfMergeEnvironment
void klfMergeEnvironment(QStringList *env, const QStringList &addvars, const QStringList &pathvars, uint actions)
QIODevice::readAll
QByteArray readAll()
KLFFilterProcess::setOutputStderr
void setOutputStderr(bool on)
Definition: klffilterprocess.cpp:239
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
KLFFilterProcess::exitCode
virtual int exitCode() const
Definition: klffilterprocess.cpp:269
KLF_ASSERT_NOT_NULL
#define KLF_ASSERT_NOT_NULL(ptr, msg, failaction)
KLFBlockProcess::startProcess
bool startProcess(QStringList cmd, QByteArray stdindata, QStringList env=QStringList())
Definition: klfblockprocess.cpp:136
KLFFilterProcess::collectedStderr
QByteArray collectedStderr() const
The collected stderr data of the process that just ran.
Definition: klffilterprocess.cpp:413
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
klfdefs.h
QFile
KLFFilterProcess::resultStatus
virtual int resultStatus() const
Definition: klffilterprocess.cpp:274
QProcess::exitCode
int exitCode() const
QByteArray
KLFBackend::klfSettings::execenv
QStringList execenv
Definition: klfbackend.h:288
QStringList
KLFFilterProcess::setOutputStdout
void setOutputStdout(bool on)
Definition: klffilterprocess.cpp:230
KLFBlockProcess::processExitStatus
int processExitStatus() const
Definition: klfblockprocess.h:87
KLFBlockProcess::setProcessAppEvents
void setProcessAppEvents(bool processAppEvents)
Definition: klfblockprocess.h:67

Generated by doxygen 1.8.16