00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <QString>
00025 #include <QFile>
00026 #include <QProcess>
00027
00028 #include <klfdefs.h>
00029
00030 #include "klfbackend.h"
00031 #include "klfblockprocess.h"
00032 #include "klffilterprocess.h"
00033
00034 #include "klffilterprocess_p.h"
00035
00036
00037
00038
00039 static QString progErrorMsg(QString progname, int exitstatus, QString stderrstr, QString stdoutstr)
00040 {
00041 QString stdouthtml = stdoutstr;
00042 QString stderrhtml = stderrstr;
00043 stdouthtml.replace("&", "&");
00044 stdouthtml.replace("<", "<");
00045 stdouthtml.replace(">", ">");
00046 stderrhtml.replace("&", "&");
00047 stderrhtml.replace("<", "<");
00048 stderrhtml.replace(">", ">");
00049
00050 if (stderrstr.isEmpty() && stdoutstr.isEmpty())
00051 return QObject::tr("<p><b>%1</b> reported an error (exit status %2). No Output was generated.</p>",
00052 "KLFBackend")
00053 .arg(progname).arg(exitstatus);
00054 if (stderrstr.isEmpty())
00055 return
00056 QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stdout output:</p>\n"
00057 "<pre>\n%3</pre>", "KLFBackend")
00058 .arg(progname).arg(exitstatus).arg(stdouthtml);
00059 if (stdoutstr.isEmpty())
00060 return
00061 QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00062 "<pre>\n%3</pre>", "KLFBackend")
00063 .arg(progname).arg(exitstatus).arg(stderrhtml);
00064
00065 return QObject::tr("<p><b>%1</b> reported an error (exit status %2). Here is full stderr output:</p>\n"
00066 "<pre>\n%3</pre><p>And here is full stdout output:</p><pre>\n%4</pre>", "KLFBackend")
00067 .arg(progname).arg(exitstatus).arg(stderrhtml).arg(stdouthtml);
00068 }
00069
00070
00071
00072
00073
00074
00075
00076
00077 KLFFilterProcessBlockProcess::KLFFilterProcessBlockProcess(KLFFilterProcess * fproc)
00078 : KLFBlockProcess(), pFproc(fproc)
00079 {
00080 }
00081 KLFFilterProcessBlockProcess::~KLFFilterProcessBlockProcess()
00082 {
00083 }
00084 QString KLFFilterProcessBlockProcess::getInterpreterPath(const QString& ext)
00085 {
00086 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00087 klfDbg("ext = " << ext) ;
00088 QMap<QString,QString> interpreters = pFproc->interpreters();
00089 QMap<QString,QString>::Iterator it = interpreters.find(ext);
00090 if (it != interpreters.end()) {
00091 return *it;
00092 }
00093 return KLFBlockProcess::getInterpreterPath(ext);
00094 }
00095
00096
00097
00098
00099
00100 struct KLFFilterProcessPrivate {
00101 KLF_PRIVATE_HEAD(KLFFilterProcess)
00102 {
00103 }
00104
00105 void init(const QString& pTitle, const KLFBackend::klfSettings *settings,
00106 const QString& rundir, bool inheritProcessEnvironment);
00107
00108 QString progTitle;
00109 QString programCwd;
00110 QStringList execEnviron;
00111
00112 QStringList argv;
00113
00114 QMap<QString,QString> interpreters;
00115
00116 bool outputStdout;
00117 bool outputStderr;
00118
00119 QByteArray *collectStdout;
00120 QByteArray *collectStderr;
00121
00122 bool processAppEvents;
00123
00124
00125 int exitStatus;
00126 int exitCode;
00127
00128 int res;
00129 QString resErrorString;
00130 };
00131
00132
00133
00134
00135 KLFFilterProcess::KLFFilterProcess(const QString& pTitle, const KLFBackend::klfSettings *settings,
00136 const QString& rundir)
00137 {
00138 KLF_INIT_PRIVATE(KLFFilterProcess) ;
00139 d->init(pTitle, settings, rundir, false);
00140 }
00141
00142 KLFFilterProcess::KLFFilterProcess(const QString& pTitle, const KLFBackend::klfSettings *settings,
00143 const QString& rundir, bool inheritProcessEnvironment)
00144 {
00145 KLF_INIT_PRIVATE(KLFFilterProcess) ;
00146 d->init(pTitle, settings, rundir, inheritProcessEnvironment);
00147 }
00148
00149 void KLFFilterProcessPrivate::init(const QString& pTitle, const KLFBackend::klfSettings *settings,
00150 const QString& rundir, bool inheritProcessEnvironment)
00151 {
00152 progTitle = pTitle;
00153
00154 collectStdout = NULL;
00155 collectStderr = NULL;
00156
00157 outputStdout = true;
00158 outputStderr = false;
00159
00160 interpreters = QMap<QString,QString>();
00161
00162 if (rundir.size()) {
00163 programCwd = rundir;
00164 }
00165 if (settings != NULL) {
00166 if (!rundir.size()) {
00167 programCwd = settings->tempdir;
00168 klfDbg("set programCwd to : "<<programCwd) ;
00169 }
00170 QStringList curenv;
00171 if (inheritProcessEnvironment) {
00172 curenv = klfCurrentEnvironment();
00173 }
00174 execEnviron = klfMergeEnvironment(curenv, settings->execenv,
00175 QStringList()<<"PATH"<<"TEXINPUTS"<<"BIBINPUTS"<<"PYTHONPATH",
00176 KlfEnvPathPrepend|KlfEnvMergeExpandVars);
00177 klfDbg("set execution environment to : "<<execEnviron) ;
00178
00179 interpreters = settings->userScriptInterpreters;
00180 }
00181
00182 processAppEvents = true;
00183
00184 exitStatus = -1;
00185 exitCode = -1;
00186 res = -1;
00187 resErrorString = QString();
00188 }
00189
00190
00191 KLFFilterProcess::~KLFFilterProcess()
00192 {
00193 KLF_DELETE_PRIVATE ;
00194 }
00195
00196
00197
00198 QString KLFFilterProcess::progTitle() const
00199 {
00200 return d->progTitle;
00201 }
00202 void KLFFilterProcess::setProgTitle(const QString& title)
00203 {
00204 d->progTitle = title;
00205 }
00206
00207 QString KLFFilterProcess::programCwd() const
00208 {
00209 return d->programCwd;
00210 }
00211 void KLFFilterProcess::setProgramCwd(const QString& cwd)
00212 {
00213 d->programCwd = cwd;
00214 }
00215
00216 QStringList KLFFilterProcess::execEnviron() const
00217 {
00218 return d->execEnviron;
00219 }
00220 void KLFFilterProcess::setExecEnviron(const QStringList& env)
00221 {
00222 d->execEnviron = env;
00223 klfDbg("set exec environment: " << d->execEnviron);
00224 }
00225 void KLFFilterProcess::addExecEnviron(const QStringList& env)
00226 {
00227 klfMergeEnvironment(& d->execEnviron, env);
00228 klfDbg("merged exec environment: " << d->execEnviron);
00229 }
00230
00231 QStringList KLFFilterProcess::argv() const
00232 {
00233 return d->argv;
00234 }
00235 void KLFFilterProcess::setArgv(const QStringList& argv)
00236 {
00237 d->argv = argv;
00238 }
00239 void KLFFilterProcess::addArgv(const QStringList& argv)
00240 {
00241 d->argv << argv;
00242 }
00243 void KLFFilterProcess::addArgv(const QString& argv)
00244 {
00245 d->argv << argv;
00246 }
00247
00248 bool KLFFilterProcess::outputStdout() const
00249 {
00250 return d->outputStdout;
00251 }
00252 void KLFFilterProcess::setOutputStdout(bool on)
00253 {
00254 d->outputStdout = on;
00255 }
00256
00257 bool KLFFilterProcess::outputStderr() const
00258 {
00259 return d->outputStderr;
00260 }
00261 void KLFFilterProcess::setOutputStderr(bool on)
00262 {
00263 d->outputStderr = on;
00264 }
00265
00266 void KLFFilterProcess::collectStdoutTo(QByteArray * stdoutstore)
00267 {
00268 setOutputStdout(true);
00269 d->collectStdout = stdoutstore;
00270 }
00271 void KLFFilterProcess::collectStderrTo(QByteArray * stderrstore)
00272 {
00273 setOutputStderr(true);
00274 d->collectStderr = stderrstore;
00275 }
00276
00277 bool KLFFilterProcess::processAppEvents()
00278 {
00279 return d->processAppEvents;
00280 }
00281
00282 void KLFFilterProcess::setProcessAppEvents(bool on)
00283 {
00284 d->processAppEvents = on;
00285 }
00286
00287 int KLFFilterProcess::exitStatus() const
00288 {
00289 return d->exitStatus;
00290 }
00291 int KLFFilterProcess::exitCode() const
00292 {
00293 return d->exitCode;
00294 }
00295
00296 int KLFFilterProcess::resultStatus() const
00297 {
00298 return d->res;
00299 }
00300 QString KLFFilterProcess::resultErrorString() const
00301 {
00302 return d->resErrorString;
00303 }
00304
00305 QMap<QString,QString> KLFFilterProcess::interpreters() const
00306 {
00307 return d->interpreters;
00308 }
00309
00310 bool KLFFilterProcess::do_run(const QByteArray& indata, const QMap<QString, QByteArray*> outdatalist)
00311 {
00312 KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00313
00314 KLFFilterProcessBlockProcess proc(this);
00315
00316 d->exitCode = 0;
00317 d->exitStatus = 0;
00318
00319 KLF_ASSERT_CONDITION(d->argv.size() > 0, "argv array is empty! No program is given!", return false; ) ;
00320
00321 proc.setWorkingDirectory(d->programCwd);
00322
00323 proc.setProcessAppEvents(d->processAppEvents);
00324
00325 klfDbg("about to exec "<<d->progTitle<<" ...") ;
00326 klfDbg("\t"<<qPrintable(d->argv.join(" "))) ;
00327 bool r = proc.startProcess(d->argv, indata, d->execEnviron);
00328 klfDbg(d->progTitle<<" returned.") ;
00329
00330 if (!r) {
00331 klfDbg("couldn't launch " << d->progTitle) ;
00332 d->res = KLFFP_NOSTART;
00333 d->resErrorString = QObject::tr("Unable to start %1 program `%2'!", "KLFBackend").arg(d->progTitle, d->argv[0]);
00334 return false;
00335 }
00336 if (!proc.processNormalExit()) {
00337 klfDbg(d->progTitle<<" did not exit normally (crashed)") ;
00338 d->exitStatus = proc.exitStatus();
00339 d->exitCode = -1;
00340 d->res = KLFFP_NOEXIT;
00341 d->resErrorString = QObject::tr("Program %1 crashed!", "KLFBackend").arg(d->progTitle);
00342 return false;
00343 }
00344 if (proc.processExitStatus() != 0) {
00345 d->exitStatus = 0;
00346 d->exitCode = proc.processExitStatus();
00347 klfDbg(d->progTitle<<" exited with code "<<d->exitCode) ;
00348 d->res = KLFFP_NOSUCCESSEXIT;
00349 d->resErrorString = progErrorMsg(d->progTitle, proc.processExitStatus(), proc.readStderrString(),
00350 proc.readStdoutString());
00351 return false;
00352 }
00353
00354 if (d->collectStdout != NULL) {
00355 *d->collectStdout = proc.getAllStdout();
00356 }
00357 if (d->collectStderr != NULL) {
00358 *d->collectStderr = proc.getAllStderr();
00359 }
00360
00361 for (QMap<QString,QByteArray*>::const_iterator it = outdatalist.begin(); it != outdatalist.end(); ++it) {
00362 QString outFileName = it.key();
00363 QByteArray * outdata = it.value();
00364
00365 KLF_ASSERT_NOT_NULL(outdata, "Given NULL outdata pointer for file "<<outFileName<<" !", return false; ) ;
00366
00367 klfDbg("Will collect output in file "<<(outFileName.isEmpty()?QString("(stdout)"):outFileName)
00368 <<" to its corresponding QByteArray pointer="<<outdata) ;
00369
00370 if (outFileName.isEmpty()) {
00371
00372 *outdata = QByteArray();
00373 if (d->outputStdout) {
00374 QByteArray stdoutdata = (d->collectStdout != NULL) ? *d->collectStdout : proc.getAllStdout();
00375 *outdata += stdoutdata;
00376 }
00377 if (d->outputStderr) {
00378 QByteArray stderrdata = (d->collectStderr != NULL) ? *d->collectStderr : proc.getAllStderr();
00379 *outdata += stderrdata;
00380 }
00381 if (outdata->isEmpty()) {
00382
00383 QString stderrstr = (!d->outputStderr) ? ("\n"+proc.readStderrString()) : QString();
00384 klfDbg(d->progTitle<<" did not provide any data. Error message: "<<stderrstr);
00385 d->res = KLFFP_NODATA;
00386 d->resErrorString = QObject::tr("Program %1 did not provide any output data.", "KLFBackend")
00387 .arg(d->progTitle) + stderrstr;
00388 return false;
00389 }
00390
00391 continue;
00392 }
00393
00394 if (!QFile::exists(outFileName)) {
00395 klfDbg("File "<<outFileName<<" did not appear after running "<<d->progTitle) ;
00396 d->res = KLFFP_NODATA;
00397 d->resErrorString = QObject::tr("Output file didn't appear after having called %1!", "KLFBackend")
00398 .arg(d->progTitle);
00399 return false;
00400 }
00401
00402
00403 QFile outfile(outFileName);
00404 r = outfile.open(QIODevice::ReadOnly);
00405 if ( ! r ) {
00406 klfDbg("File "<<outFileName<<" cannot be read (after running "<<d->progTitle<<")") ;
00407 d->res = KLFFP_DATAREADFAIL;
00408 d->resErrorString = QObject::tr("Can't read file '%1'!\n", "KLFBackend").arg(outFileName);
00409 return false;
00410 }
00411
00412 *outdata = outfile.readAll();
00413 klfDbg("Read file "<<outFileName<<", got data, length="<<outdata->size());
00414 }
00415
00416 klfDbg(d->progTitle<<" was successfully run and output successfully retrieved.") ;
00417
00418
00419 d->exitStatus = 0;
00420 d->exitCode = 0;
00421 d->res = KLFFP_NOERR;
00422 d->resErrorString = QString();
00423
00424 return true;
00425 }
00426
00427
00428 QByteArray KLFFilterProcess::collectedStdout() const
00429 {
00430 if (!d->outputStdout || d->collectStdout == NULL) {
00431 return QByteArray();
00432 }
00433 return *d->collectStdout;
00434 }
00435 QByteArray KLFFilterProcess::collectedStderr() const
00436 {
00437 if (!d->outputStderr || d->collectStderr == NULL) {
00438 return QByteArray();
00439 }
00440 return *d->collectStderr;
00441 }