#include "support/debug.h"
#include "support/filetools.h"
+#include "support/gettext.h"
#include "support/lstrings.h"
#include "support/qstring_helpers.h"
#include "support/Systemcall.h"
#include "support/os.h"
#include "support/ProgressInterface.h"
+#include "LyX.h"
#include "LyXRC.h"
#include <cstdlib>
void warning(QString const &, QString const &) {}
void toggleWarning(QString const &, QString const &, QString const &) {}
- void error(QString const &, QString const &) {}
+ void error(QString const &, QString const &, QString const &) {}
void information(QString const &, QString const &) {}
+ int prompt(docstring const &, docstring const &, int default_but, int,
+ docstring const &, docstring const &) { return default_but; }
};
-static ProgressInterface* progress_instance = 0;
+static ProgressInterface * progress_instance = 0;
void ProgressInterface::setInstance(ProgressInterface* p)
{
}
-ProgressInterface* ProgressInterface::instance()
+ProgressInterface * ProgressInterface::instance()
{
if (!progress_instance) {
static ProgressDummy dummy;
// Reuse of instance
#ifndef USE_QPROCESS
int Systemcall::startscript(Starttype how, string const & what,
- std::string const & path, bool /*process_events*/)
+ string const & path, string const & lpath,
+ bool /*process_events*/)
{
- string const python_call = "python -tt";
- string command = to_filesystem8bit(from_utf8(latexEnvCmdPrefix(path)));
-
- if (prefixIs(what, python_call))
- command += os::python() + what.substr(python_call.length());
- else
- command += what;
+ string command =
+ to_filesystem8bit(from_utf8(latexEnvCmdPrefix(path, lpath)))
+ + commandPrep(what);
if (how == DontWait) {
switch (os::shell()) {
/*
* This is a parser that (mostly) mimics the behavior of a posix shell as
* regards quoting, but its output is tailored for being processed by QProcess.
- * Note that shell metacharacters are not parsed and only output redirection
- * is taken into account.
+ * Note that shell metacharacters are not parsed.
*
* The escape character is the backslash.
* A backslash that is not quoted preserves the literal value of the following
* "\a" -> "\a"
* "a\"b" -> "a"""b"
*/
-string const parsecmd(string const & incmd, string & outfile, string & errfile)
+string const parsecmd(string const & incmd, string & infile, string & outfile,
+ string & errfile)
{
bool in_single_quote = false;
bool in_double_quote = false;
bool escaped = false;
string const python_call = "python -tt";
- vector<string> outcmd(3);
+ vector<string> outcmd(4);
size_t start = 0;
if (prefixIs(incmd, python_call)) {
outcmd[o] = rtrim(outcmd[o], "1");
o = 1;
}
+ } else if (c == '<' && !(in_double_quote || escaped)) {
+ o = 3;
} else {
if (escaped && in_double_quote)
outcmd[o] += '\\';
escaped = false;
}
}
+ infile = trim(outcmd[3], " \"");
outfile = trim(outcmd[1], " \"");
errfile = trim(outcmd[2], " \"");
return trim(outcmd[0]);
int Systemcall::startscript(Starttype how, string const & what,
- string const & path, bool process_events)
+ string const & path, string const & lpath,
+ bool process_events)
{
- lyxerr << "\nRunning: " << what << endl;
+ string const what_ss = commandPrep(what);
+ if (verbose)
+ lyxerr << "\nRunning: " << what_ss << endl;
+ else
+ LYXERR(Debug::INFO,"Running: " << what_ss);
+ string infile;
string outfile;
string errfile;
- QString cmd = QString::fromLocal8Bit(
- parsecmd(what, outfile, errfile).c_str());
-
- SystemcallPrivate d(outfile, errfile);
-
-
- d.startProcess(cmd, path);
+ QString const cmd = QString::fromLocal8Bit(
+ parsecmd(what_ss, infile, outfile, errfile).c_str());
+
+ SystemcallPrivate d(infile, outfile, errfile);
+
+#ifdef Q_OS_WIN32
+ // QProcess::startDetached cannot provide environment variables. When the
+ // environment variables are set using the latexEnvCmdPrefix and the process
+ // is started with QProcess::startDetached, a console window is shown every
+ // time a viewer is started. To avoid this, we fall back on Windows to the
+ // original implementation that creates a QProcess object.
+ d.startProcess(cmd, path, lpath, false);
if (!d.waitWhile(SystemcallPrivate::Starting, process_events, -1)) {
LYXERR0("Systemcall: '" << cmd << "' did not start!");
LYXERR0("error " << d.errorMessage());
return 10;
}
-
if (how == DontWait) {
- QProcess* released = d.releaseProcess();
- (void) released; // TODO who deletes it?
+ d.releaseProcess();
+ return 0;
+ }
+#else
+ d.startProcess(cmd, path, lpath, how == DontWait);
+ if (how == DontWait && d.state == SystemcallPrivate::Running)
return 0;
+
+ if (d.state == SystemcallPrivate::Error
+ || !d.waitWhile(SystemcallPrivate::Starting, process_events, -1)) {
+ LYXERR0("Systemcall: '" << cmd << "' did not start!");
+ LYXERR0("error " << d.errorMessage());
+ return 10;
}
+#endif
if (!d.waitWhile(SystemcallPrivate::Running, process_events,
os::timeout_min() * 60 * 1000)) {
}
-SystemcallPrivate::SystemcallPrivate(std::string const & of,
- std::string const & ef) :
- process_(new QProcess),
- out_index_(0),
- err_index_(0),
- out_file_(of),
- err_file_(ef),
- process_events_(false)
+SystemcallPrivate::SystemcallPrivate(std::string const & sf, std::string const & of,
+ std::string const & ef)
+ : state(Error), process_(new QProcess), out_index_(0), err_index_(0),
+ in_file_(sf), out_file_(of), err_file_(ef), process_events_(false)
{
+ if (!in_file_.empty())
+ process_->setStandardInputFile(QString::fromLocal8Bit(in_file_.c_str()));
if (!out_file_.empty()) {
if (out_file_[0] == '&') {
if (subst(out_file_, " ", "") == "&2"
connect(process_, SIGNAL(readyReadStandardOutput()), SLOT(stdOut()));
connect(process_, SIGNAL(readyReadStandardError()), SLOT(stdErr()));
+#if QT_VERSION >= 0x050600
+ connect(process_, SIGNAL(errorOccurred(QProcess::ProcessError)), SLOT(processError(QProcess::ProcessError)));
+#else
connect(process_, SIGNAL(error(QProcess::ProcessError)), SLOT(processError(QProcess::ProcessError)));
+#endif
connect(process_, SIGNAL(started()), this, SLOT(processStarted()));
connect(process_, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus)));
}
-void SystemcallPrivate::startProcess(QString const & cmd, string const & path)
+void SystemcallPrivate::startProcess(QString const & cmd, string const & path,
+ string const & lpath, bool detached)
{
cmd_ = cmd;
- if (process_) {
+ if (detached) {
+ state = SystemcallPrivate::Running;
+ if (!QProcess::startDetached(toqstr(latexEnvCmdPrefix(path, lpath)) + cmd_)) {
+ state = SystemcallPrivate::Error;
+ return;
+ }
+ QProcess* released = releaseProcess();
+ delete released;
+ } else if (process_) {
state = SystemcallPrivate::Starting;
- process_->start(toqstr(latexEnvCmdPrefix(path)) + cmd_);
+ process_->start(toqstr(latexEnvCmdPrefix(path, lpath)) + cmd_);
}
}
void SystemcallPrivate::processEvents()
{
- if(process_events_) {
+ if (process_events_) {
QCoreApplication::processEvents(/*QEventLoop::ExcludeUserInputEvents*/);
}
}
}
+namespace {
+
+bool queryStopCommand(QString const & cmd)
+{
+ docstring text = bformat(_(
+ "The command\n%1$s\nhas not yet completed.\n\n"
+ "Do you want to stop it?"), qstring_to_ucs4(cmd));
+ return ProgressInterface::instance()->prompt(_("Stop command?"), text,
+ 1, 1, _("&Stop it"), _("Let it &run")) == 0;
+}
+
+}
+
+
bool SystemcallPrivate::waitWhile(State waitwhile, bool process_events, int timeout)
{
if (!process_)
return false;
+ bool timedout = false;
process_events_ = process_events;
// Block GUI while waiting,
if (!process_events_) {
if (waitwhile == Starting)
return process_->waitForStarted(timeout);
- if (waitwhile == Running)
- return process_->waitForFinished(timeout);
+ if (waitwhile == Running) {
+ int bump = 2;
+ while (!timedout) {
+ if (process_->waitForFinished(timeout))
+ return true;
+ bool stop = queryStopCommand(cmd_);
+ // The command may have finished in the meantime
+ if (process_->state() == QProcess::NotRunning)
+ return true;
+ if (stop) {
+ timedout = true;
+ process_->kill();
+ } else {
+ timeout *= bump;
+ bump = 3;
+ }
+ }
+ }
return false;
}
return state != Error;
}
- // process events while waiting whith timeout
+ // process events while waiting with timeout
QTime timer;
timer.start();
- while (state == waitwhile && state != Error && timer.elapsed() < timeout) {
+ while (state == waitwhile && state != Error && !timedout) {
waitAndProcessEvents();
+ if (timer.elapsed() > timeout) {
+ bool stop = queryStopCommand(cmd_);
+ // The command may have finished in the meantime
+ if (process_->state() == QProcess::NotRunning)
+ break;
+ if (stop) {
+ timedout = true;
+ process_->kill();
+ } else
+ timeout *= 3;
+ }
}
- return (state != Error) && (timer.elapsed() < timeout);
+ return (state != Error) && !timedout;
}
break;
case QProcess::UnknownError:
default:
- message = "An unknown error occured.";
+ message = "An unknown error occurred.";
break;
}
return message;
int SystemcallPrivate::exitCode()
{
- if (!process_)
+ // From Qt's documentation, in regards to QProcess::exitCode(),
+ // "This value is not valid unless exitStatus() returns NormalExit"
+ if (!process_ || process_->exitStatus() != QProcess::NormalExit)
return -1;
return process_->exitCode();