[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 }
QMap< QString, QString > userScriptInterpreters
Definition: klfbackend.h:300
Defines the KLFBlockProcess class.
void setOutputStderr(bool on)
#define KLF_PRIVATE_HEAD(ClassName)
virtual QString getInterpreterPath(const QString &ext)
The interpter path to use for the given extension.
virtual int exitStatus() const
void klfMergeEnvironment(QStringList *env, const QStringList &addvars, const QStringList &pathvars, uint actions)
int size() const
bool isEmpty() const
#define klfDbg(streamableItems)
#define KLF_DEBUG_BLOCK(msg)
virtual int resultStatus() const
void collectStdoutTo(QByteArray *stdoutstore)
bool exists() const
QByteArray collectedStderr() const
The collected stderr data of the process that just ran.
A QProcess subclass for code-blocking process execution.
virtual int exitCode() const
QString tr(const char *sourceText, const char *disambiguation, int n)
void collectStderrTo(QByteArray *stderrstore)
#define KLF_ASSERT_NOT_NULL(ptr, msg, failaction)
#define KLF_DELETE_PRIVATE
QByteArray collectedStdout() const
The collected stdout data of the process that just ran.
virtual QString resultErrorString() const
bool isEmpty() const
QByteArray readAll()
iterator end()
virtual bool open(OpenMode mode)
iterator begin()
#define KLF_FUNC_NAME
void setOutputStdout(bool on)
virtual bool do_run(const QByteArray &indata, const QMap< QString, QByteArray * > outdatalist)
Actually run the process.
Definition of class KLFBackend.
const Key key(const T &value, const Key &defaultKey) const
QString & replace(int position, int n, QChar after)
#define KLF_INIT_PRIVATE(ClassName)
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
int size() const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
KLF_EXPORT QStringList klfCurrentEnvironment()
iterator find(const Key &key)
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:218
const T value(const Key &key, const T &defaultValue) const
void setProcessAppEvents(bool processEvents)

Generated by doxygen 1.8.15