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);
51 static void killProcess(QProcess * p)
54 p->closeReadChannel(QProcess::StandardOutput);
55 p->closeReadChannel(QProcess::StandardError);
66 int Systemcall::startscript(Starttype how, string const & what)
68 string command = what;
70 if (how == DontWait) {
71 switch (os::shell()) {
76 command = "start /min " + command;
81 return ::system(command.c_str());
88 string const parsecmd(string const & cmd, string & outfile)
93 for (size_t i = 0; i < cmd.length(); ++i) {
95 if (c == '"' && !escaped)
97 else if (c == '\\' && !escaped)
99 else if (c == '>' && !(inquote || escaped)) {
100 outfile = trim(cmd.substr(i + 1), " \"");
101 return trim(cmd.substr(0, i));
113 int Systemcall::startscript(Starttype how, string const & what)
116 QString cmd = toqstr(parsecmd(what, outfile));
117 QProcess * process = new QProcess;
118 SystemcallPrivate d(process);
119 if (!outfile.empty()) {
120 // Check whether we have to simply throw away the output.
121 if (outfile != os::nulldev())
122 process->setStandardOutputFile(toqstr(outfile));
123 } else if (os::is_terminal(os::STDOUT))
125 if (os::is_terminal(os::STDERR))
129 bool processEvents = false;
131 if (!d.waitWhile(SystemcallPrivate::Starting, processEvents, 3000)) {
132 LYXERR0("QProcess " << cmd << " did not start!");
133 LYXERR0("error " << d.errorMessage());
137 if (how == DontWait) {
138 // TODO delete process later
142 if (!d.waitWhile(SystemcallPrivate::Running, processEvents, 180000)) {
143 LYXERR0("QProcess " << cmd << " did not finished!");
144 LYXERR0("error " << d.errorMessage());
145 LYXERR0("status " << d.exitStatusMessage());
149 int const exit_code = process->exitCode();
151 LYXERR0("QProcess " << cmd << " finished!");
152 LYXERR0("error " << exit_code << ": " << d.errorMessage());
155 // If the output has been redirected, we write it all at once.
156 // Even if we are not running in a terminal, the output could go
157 // to some log file, for example ~/.xsession-errors on *nix.
158 if (!os::is_terminal(os::STDOUT) && outfile.empty())
159 cout << fromqstr(QString::fromLocal8Bit(
160 process->readAllStandardOutput().data()));
161 if (!os::is_terminal(os::STDERR))
162 cerr << fromqstr(QString::fromLocal8Bit(
163 process->readAllStandardError().data()));
165 killProcess(process);
171 SystemcallPrivate::SystemcallPrivate(QProcess * proc) : proc_(proc), outindex_(0),
172 errindex_(0), showout_(false), showerr_(false)
174 connect(proc, SIGNAL(readyReadStandardOutput()), SLOT(stdOut()));
175 connect(proc, SIGNAL(readyReadStandardError()), SLOT(stdErr()));
176 connect(proc, SIGNAL(error(QProcess::ProcessError)), SLOT(processError(QProcess::ProcessError)));
177 connect(proc, SIGNAL(started()), this, SLOT(processStarted()));
178 connect(proc, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus)));
183 void SystemcallPrivate::startProcess(const QString& cmd)
185 state = SystemcallPrivate::Starting;
190 void SystemcallPrivate::waitAndProcessEvents()
192 Sleep::millisec(100);
193 QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
197 bool SystemcallPrivate::waitWhile(State waitwhile, bool processEvents, int timeout)
199 // Block GUI while waiting,
200 // relay on QProcess' wait functions
201 if (!processEvents) {
202 if (waitwhile == Starting)
203 return proc_->waitForStarted(timeout);
204 if (waitwhile == Running)
205 return proc_->waitForFinished(timeout);
209 // process events while waiting, no timeout
211 while (state == waitwhile && state != Error) {
212 waitAndProcessEvents();
214 return state != Error;
217 // process events while waiting whith timeout
220 while (state == waitwhile && state != Error && timer.elapsed() < timeout) {
221 waitAndProcessEvents();
223 return (state != Error) && (timer.elapsed() < timeout);
227 SystemcallPrivate::~SystemcallPrivate()
230 outdata_[outindex_] = '\0';
236 errdata_[errindex_] = '\0';
244 void SystemcallPrivate::stdOut()
248 proc_->setReadChannel(QProcess::StandardOutput);
249 while (proc_->getChar(&c)) {
250 outdata_[outindex_++] = c;
251 if (c == '\n' || outindex_ + 1 == bufsize_) {
252 outdata_[outindex_] = '\0';
261 void SystemcallPrivate::stdErr()
265 proc_->setReadChannel(QProcess::StandardError);
266 while (proc_->getChar(&c)) {
267 errdata_[errindex_++] = c;
268 if (c == '\n' || errindex_ + 1 == bufsize_) {
269 errdata_[errindex_] = '\0';
278 void SystemcallPrivate::processError(QProcess::ProcessError err)
284 QString SystemcallPrivate::errorMessage() const
287 switch (proc_->error()) {
288 case QProcess::FailedToStart:
289 message = "The process failed to start. Either the invoked program is missing, "
290 "or you may have insufficient permissions to invoke the program.";
292 case QProcess::Crashed:
293 message = "The process crashed some time after starting successfully.";
295 case QProcess::Timedout:
296 message = "The process timed out. It might be restarted automatically.";
298 case QProcess::WriteError:
299 message = "An error occurred when attempting to write to the process-> For example, "
300 "the process may not be running, or it may have closed its input channel.";
302 case QProcess::ReadError:
303 message = "An error occurred when attempting to read from the process-> For example, "
304 "the process may not be running.";
306 case QProcess::UnknownError:
308 message = "An unknown error occured.";
315 void SystemcallPrivate::processStarted()
318 // why do we get two started signals?
319 //disconnect(proc_, SIGNAL(started()), this, SLOT(processStarted()));
323 void SystemcallPrivate::processFinished(int, QProcess::ExitStatus status)
329 QString SystemcallPrivate::exitStatusMessage() const
332 switch (proc_->exitStatus()) {
333 case QProcess::NormalExit:
334 message = "The process exited normally.";
336 case QProcess::CrashExit:
337 message = "The process crashed.";
340 message = "Unknown exit state.";
347 #include "moc_SystemcallPrivate.cpp"
350 } // namespace support