[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
src/klfbackend/klffilterprocess.cpp
00001 /***************************************************************************
00002  *   file klffilterprocess.cpp
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2011 by Philippe Faist
00005  *   philippe.faist at bluewin.ch
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 /* $Id$ */
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 // Utility function
00039 static QString progErrorMsg(QString progname, int exitstatus, QString stderrstr, QString stdoutstr)
00040 {
00041   QString stdouthtml = stdoutstr;
00042   QString stderrhtml = stderrstr;
00043   stdouthtml.replace("&", "&amp;");
00044   stdouthtml.replace("<", "&lt;");
00045   stdouthtml.replace(">", "&gt;");
00046   stderrhtml.replace("&", "&amp;");
00047   stderrhtml.replace("<", "&lt;");
00048   stderrhtml.replace(">", "&gt;");
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   // these fields are set after calling run()
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       // empty outFileName means to use standard output
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         // no data
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       // read standard output to buffer, continue with other possible outputs
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     // read output file into outdata
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   // all OK
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 }

Generated by doxygen 1.7.6.1. The KLatexFormula website is hosted on sourceforge.net