3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Angus Leeming
8 * \author Enrico Forestieri
9 * \author Peter Kuemmel
11 * Full author contact details are available in file CREDITS.
16 #include "support/debug.h"
17 #include "support/lstrings.h"
18 #include "support/qstring_helpers.h"
19 #include "support/Systemcall.h"
20 #include "support/SystemcallPrivate.h"
21 #include "support/os.h"
30 #include <QCoreApplication>
36 struct Sleep : QThread
38 static void millisec(unsigned long ms)
40 QThread::usleep(ms * 1000);
55 int Systemcall::startscript(Starttype how, string const & what)
57 string command = what;
59 if (how == DontWait) {
60 switch (os::shell()) {
65 command = "start /min " + command;
70 return ::system(command.c_str());
77 string const parsecmd(string const & cmd, string & outfile)
82 for (size_t i = 0; i < cmd.length(); ++i) {
84 if (c == '"' && !escaped)
86 else if (c == '\\' && !escaped)
88 else if (c == '>' && !(inquote || escaped)) {
89 outfile = trim(cmd.substr(i + 1), " \"");
90 return trim(cmd.substr(0, i));
102 int Systemcall::startscript(Starttype how, string const & what)
105 QString cmd = toqstr(parsecmd(what, outfile));
106 SystemcallPrivate d(outfile);
108 bool processEvents = false;
111 if (!d.waitWhile(SystemcallPrivate::Starting, processEvents, 3000)) {
112 LYXERR0("QProcess " << cmd << " did not start!");
113 LYXERR0("error " << d.errorMessage());
117 if (how == DontWait) {
118 // TODO delete process later
122 if (!d.waitWhile(SystemcallPrivate::Running, processEvents, 180000)) {
123 LYXERR0("QProcess " << cmd << " did not finished!");
124 LYXERR0("error " << d.errorMessage());
125 LYXERR0("status " << d.exitStatusMessage());
129 int const exit_code = d.exitCode();
131 LYXERR0("QProcess " << cmd << " finished!");
132 LYXERR0("error " << exit_code << ": " << d.errorMessage());
142 SystemcallPrivate::SystemcallPrivate(const std::string& of) :
143 proc_(new QProcess), outindex_(0),
144 errindex_(0), showout_(false), showerr_(false), outfile(of)
146 if (!outfile.empty()) {
147 // Check whether we have to simply throw away the output.
148 if (outfile != os::nulldev())
149 proc_->setStandardOutputFile(toqstr(outfile));
150 } else if (os::is_terminal(os::STDOUT))
152 if (os::is_terminal(os::STDERR))
155 connect(proc_, SIGNAL(readyReadStandardOutput()), SLOT(stdOut()));
156 connect(proc_, SIGNAL(readyReadStandardError()), SLOT(stdErr()));
157 connect(proc_, SIGNAL(error(QProcess::ProcessError)), SLOT(processError(QProcess::ProcessError)));
158 connect(proc_, SIGNAL(started()), this, SLOT(processStarted()));
159 connect(proc_, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus)));
164 void SystemcallPrivate::startProcess(const QString& cmd)
166 state = SystemcallPrivate::Starting;
171 void SystemcallPrivate::waitAndProcessEvents()
173 Sleep::millisec(100);
174 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
178 bool SystemcallPrivate::waitWhile(State waitwhile, bool processEvents, int timeout)
180 // Block GUI while waiting,
181 // relay on QProcess' wait functions
182 if (!processEvents) {
183 if (waitwhile == Starting)
184 return proc_->waitForStarted(timeout);
185 if (waitwhile == Running)
186 return proc_->waitForFinished(timeout);
190 // process events while waiting, no timeout
192 while (state == waitwhile && state != Error) {
193 waitAndProcessEvents();
195 return state != Error;
198 // process events while waiting whith timeout
201 while (state == waitwhile && state != Error && timer.elapsed() < timeout) {
202 waitAndProcessEvents();
204 return (state != Error) && (timer.elapsed() < timeout);
209 SystemcallPrivate::~SystemcallPrivate()
212 outdata_[outindex_] = '\0';
218 errdata_[errindex_] = '\0';
226 void SystemcallPrivate::flush()
228 // If the output has been redirected, we write it all at once.
229 // Even if we are not running in a terminal, the output could go
230 // to some log file, for example ~/.xsession-errors on *nix.
231 if (!os::is_terminal(os::STDOUT) && outfile.empty())
232 cout << fromqstr(QString::fromLocal8Bit(
233 proc_->readAllStandardOutput().data()));
234 if (!os::is_terminal(os::STDERR))
235 cerr << fromqstr(QString::fromLocal8Bit(
236 proc_->readAllStandardError().data()));
239 void SystemcallPrivate::stdOut()
243 proc_->setReadChannel(QProcess::StandardOutput);
244 while (proc_->getChar(&c)) {
245 outdata_[outindex_++] = c;
246 if (c == '\n' || outindex_ + 1 == bufsize_) {
247 outdata_[outindex_] = '\0';
256 void SystemcallPrivate::stdErr()
260 proc_->setReadChannel(QProcess::StandardError);
261 while (proc_->getChar(&c)) {
262 errdata_[errindex_++] = c;
263 if (c == '\n' || errindex_ + 1 == bufsize_) {
264 errdata_[errindex_] = '\0';
273 void SystemcallPrivate::processError(QProcess::ProcessError err)
279 QString SystemcallPrivate::errorMessage() const
282 switch (proc_->error()) {
283 case QProcess::FailedToStart:
284 message = "The process failed to start. Either the invoked program is missing, "
285 "or you may have insufficient permissions to invoke the program.";
287 case QProcess::Crashed:
288 message = "The process crashed some time after starting successfully.";
290 case QProcess::Timedout:
291 message = "The process timed out. It might be restarted automatically.";
293 case QProcess::WriteError:
294 message = "An error occurred when attempting to write to the process-> For example, "
295 "the process may not be running, or it may have closed its input channel.";
297 case QProcess::ReadError:
298 message = "An error occurred when attempting to read from the process-> For example, "
299 "the process may not be running.";
301 case QProcess::UnknownError:
303 message = "An unknown error occured.";
310 void SystemcallPrivate::processStarted()
313 // why do we get two started signals?
314 //disconnect(proc_, SIGNAL(started()), this, SLOT(processStarted()));
318 void SystemcallPrivate::processFinished(int, QProcess::ExitStatus status)
324 QString SystemcallPrivate::exitStatusMessage() const
327 switch (proc_->exitStatus()) {
328 case QProcess::NormalExit:
329 message = "The process exited normally.";
331 case QProcess::CrashExit:
332 message = "The process crashed.";
335 message = "Unknown exit state.";
341 int SystemcallPrivate::exitCode()
343 return proc_->exitCode();
347 void SystemcallPrivate::killProcess()
352 void SystemcallPrivate::killProcess(QProcess * p)
355 p->closeReadChannel(QProcess::StandardOutput);
356 p->closeReadChannel(QProcess::StandardError);
363 #include "moc_SystemcallPrivate.cpp"
366 } // namespace support