[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
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  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 
135 KLFFilterProcess::KLFFilterProcess(const QString& pTitle, const KLFBackend::klfSettings *settings,
136  const QString& rundir)
137 {
139  d->init(pTitle, settings, rundir, false);
140 }
141 
142 KLFFilterProcess::KLFFilterProcess(const QString& pTitle, const KLFBackend::klfSettings *settings,
143  const QString& rundir, bool inheritProcessEnvironment)
144 {
146  d->init(pTitle, settings, rundir, inheritProcessEnvironment);
147 }
148 
149 void 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 
191 KLFFilterProcess::~KLFFilterProcess()
192 {
194 }
195 
196 
197 
198 QString KLFFilterProcess::progTitle() const
199 {
200  return d->progTitle;
201 }
202 void KLFFilterProcess::setProgTitle(const QString& title)
203 {
204  d->progTitle = title;
205 }
206 
207 QString KLFFilterProcess::programCwd() const
208 {
209  return d->programCwd;
210 }
211 void KLFFilterProcess::setProgramCwd(const QString& cwd)
212 {
213  d->programCwd = cwd;
214 }
215 
216 QStringList KLFFilterProcess::execEnviron() const
217 {
218  return d->execEnviron;
219 }
220 void KLFFilterProcess::setExecEnviron(const QStringList& env)
221 {
222  d->execEnviron = env;
223  klfDbg("set exec environment: " << d->execEnviron);
224 }
225 void KLFFilterProcess::addExecEnviron(const QStringList& env)
226 {
227  klfMergeEnvironment(& d->execEnviron, env);
228  klfDbg("merged exec environment: " << d->execEnviron);
229 }
230 
231 QStringList KLFFilterProcess::argv() const
232 {
233  return d->argv;
234 }
235 void KLFFilterProcess::setArgv(const QStringList& argv)
236 {
237  d->argv = argv;
238 }
239 void KLFFilterProcess::addArgv(const QStringList& argv)
240 {
241  d->argv << argv;
242 }
243 void KLFFilterProcess::addArgv(const QString& argv)
244 {
245  d->argv << argv;
246 }
247 
248 bool KLFFilterProcess::outputStdout() const
249 {
250  return d->outputStdout;
251 }
253 {
254  d->outputStdout = on;
255 }
256 
257 bool 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 
305 QMap<QString,QString> KLFFilterProcess::interpreters() const
306 {
307  return d->interpreters;
308 }
309 
310 bool KLFFilterProcess::do_run(const QByteArray& indata, const QMap<QString, QByteArray*> outdatalist)
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 }
klfdefs.h
KLFBackend::klfSettings::userScriptInterpreters
QMap< QString, QString > userScriptInterpreters
Definition: klfbackend.h:300
KLFFilterProcess::collectStderrTo
void collectStderrTo(QByteArray *stderrstore)
Definition: klffilterprocess.cpp:271
QString::size
int size() const
KLFBlockProcess::getInterpreterPath
virtual QString getInterpreterPath(const QString &ext)
The interpter path to use for the given extension.
Definition: klfblockprocess.cpp:118
KLFFilterProcess::resultErrorString
virtual QString resultErrorString() const
Definition: klffilterprocess.cpp:300
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:287
KLFFilterProcess::setProcessAppEvents
void setProcessAppEvents(bool processEvents)
Definition: klffilterprocess.cpp:282
KLF_PRIVATE_HEAD
#define KLF_PRIVATE_HEAD(ClassName)
KLF_FUNC_NAME
#define KLF_FUNC_NAME
QFile::exists
bool exists() const
QMap::end
iterator end()
klfDbg
#define klfDbg(streamableItems)
QMap::find
iterator find(const Key &key)
KLF_DEBUG_BLOCK
#define KLF_DEBUG_BLOCK(msg)
QString
QString::isEmpty
bool isEmpty() const
KLF_DELETE_PRIVATE
#define KLF_DELETE_PRIVATE
KLFFilterProcess::processAppEvents
bool processAppEvents()
Definition: klffilterprocess.cpp:277
KLFFilterProcess
Definition: klffilterprocess.h:47
QMap::key
const Key key(const T &value, const Key &defaultKey) const
KLFBlockProcess
A QProcess subclass for code-blocking process execution.
Definition: klfblockprocess.h:56
QString::replace
QString & replace(int position, int n, QChar after)
KLFFilterProcess::collectStdoutTo
void collectStdoutTo(QByteArray *stdoutstore)
Definition: klffilterprocess.cpp:266
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:227
QMap::const_iterator
KLFFilterProcess::collectedStdout
QByteArray collectedStdout() const
The collected stdout data of the process that just ran.
Definition: klffilterprocess.cpp:428
klfCurrentEnvironment
KLF_EXPORT QStringList klfCurrentEnvironment()
KLFBackend::klfSettings
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:218
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:310
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:261
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
KLFFilterProcess::exitCode
virtual int exitCode() const
Definition: klffilterprocess.cpp:291
KLF_ASSERT_NOT_NULL
#define KLF_ASSERT_NOT_NULL(ptr, msg, failaction)
KLFFilterProcess::collectedStderr
QByteArray collectedStderr() const
The collected stderr data of the process that just ran.
Definition: klffilterprocess.cpp:435
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
QFile
KLFFilterProcess::resultStatus
virtual int resultStatus() const
Definition: klffilterprocess.cpp:296
QByteArray
KLFBackend::klfSettings::execenv
QStringList execenv
Definition: klfbackend.h:289
QStringList
KLFFilterProcess::setOutputStdout
void setOutputStdout(bool on)
Definition: klffilterprocess.cpp:252

Generated by doxygen 1.8.17