[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 }
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 }
virtual bool do_run(const QByteArray &indata, const QMap< QString, QByteArray *> outdatalist)
Actually run the process.
QMap< QString, QString > userScriptInterpreters
Definition: klfbackend.h:300
Defines the KLFBlockProcess class.
QString readStdoutString()
void setOutputStderr(bool on)
#define KLF_PRIVATE_HEAD(ClassName)
virtual QString getInterpreterPath(const QString &ext)
The interpter path to use for the given extension.
void setProcessAppEvents(bool processAppEvents)
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)
bool processNormalExit() const
#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()
QString readStderrString()
iterator end()
virtual bool open(OpenMode mode)
iterator begin()
#define KLF_FUNC_NAME
void setOutputStdout(bool on)
void setWorkingDirectory(const QString &dir)
bool startProcess(QStringList cmd, QByteArray stdindata, QStringList env=QStringList())
Definition of class KLFBackend.
const Key key(const T &value, const Key &defaultKey) const
QString & replace(int position, int n, QChar after)
QByteArray getAllStderr()
#define KLF_INIT_PRIVATE(ClassName)
#define KLF_ASSERT_CONDITION(expr, msg, failaction)
int processExitStatus() const
int size() const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const
iterator find(const Key &key)
QProcess::ExitStatus exitStatus() const
int exitCode() const
General settings for KLFBackend::getLatexFormula()
Definition: klfbackend.h:218
const T value(const Key &key, const T &defaultValue) const
QByteArray getAllStdout()
void setProcessAppEvents(bool processEvents)

Generated by doxygen 1.8.13