Properly account for output redirection with QProcess.

git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/branches/BRANCH_2_0_X@40620 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Enrico Forestieri 2012-01-16 00:36:13 +00:00
parent 3ae401e5bc
commit 7149006dca
3 changed files with 94 additions and 33 deletions

View File

@ -128,8 +128,10 @@ int Systemcall::startscript(Starttype how, string const & what,
namespace { namespace {
/* /*
* This is a parser that (mostly) mimics the behavior of a posix shell but * This is a parser that (mostly) mimics the behavior of a posix shell as
* its output is tailored for being processed by QProcess. * 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.
* *
* The escape character is the backslash. * The escape character is the backslash.
* A backslash that is not quoted preserves the literal value of the following * A backslash that is not quoted preserves the literal value of the following
@ -162,58 +164,67 @@ namespace {
* "\a" -> "\a" * "\a" -> "\a"
* "a\"b" -> "a"""b" * "a\"b" -> "a"""b"
*/ */
string const parsecmd(string const & inputcmd, string & outfile) string const parsecmd(string const & incmd, string & outfile, string & errfile)
{ {
bool in_single_quote = false; bool in_single_quote = false;
bool in_double_quote = false; bool in_double_quote = false;
bool escaped = false; bool escaped = false;
string const python_call = "python -tt"; string const python_call = "python -tt";
string cmd; vector<string> outcmd(3);
int start = 0; size_t start = 0;
if (prefixIs(inputcmd, python_call)) { if (prefixIs(incmd, python_call)) {
cmd = os::python(); outcmd[0] = os::python();
start = python_call.length(); start = python_call.length();
} }
for (size_t i = start; i < inputcmd.length(); ++i) { for (size_t i = start, o = 0; i < incmd.length(); ++i) {
char c = inputcmd[i]; char c = incmd[i];
if (c == '\'') { if (c == '\'') {
if (in_double_quote || escaped) { if (in_double_quote || escaped) {
if (in_double_quote && escaped) if (in_double_quote && escaped)
cmd += '\\'; outcmd[o] += '\\';
cmd += c; outcmd[o] += c;
} else } else
in_single_quote = !in_single_quote; in_single_quote = !in_single_quote;
escaped = false; escaped = false;
continue; continue;
} }
if (in_single_quote) { if (in_single_quote) {
cmd += c; outcmd[o] += c;
continue; continue;
} }
if (c == '"') { if (c == '"') {
if (escaped) { if (escaped) {
cmd += "\"\"\""; // Don't triple double-quotes for redirection
// files as these won't be parsed by QProcess
outcmd[o] += string(o ? "\"" : "\"\"\"");
escaped = false; escaped = false;
} else { } else {
cmd += c; outcmd[o] += c;
in_double_quote = !in_double_quote; in_double_quote = !in_double_quote;
} }
} else if (c == '\\' && !escaped) { } else if (c == '\\' && !escaped) {
escaped = !escaped; escaped = !escaped;
} else if (c == '>' && !(in_double_quote || escaped)) { } else if (c == '>' && !(in_double_quote || escaped)) {
outfile = trim(inputcmd.substr(i + 1), " \""); if (suffixIs(outcmd[o], " 2")) {
return trim(cmd); outcmd[o] = rtrim(outcmd[o], "2");
o = 2;
} else {
if (suffixIs(outcmd[o], " 1"))
outcmd[o] = rtrim(outcmd[o], "1");
o = 1;
}
} else { } else {
if (escaped && in_double_quote) if (escaped && in_double_quote)
cmd += '\\'; outcmd[o] += '\\';
cmd += c; outcmd[o] += c;
escaped = false; escaped = false;
} }
} }
outfile.erase(); outfile = trim(outcmd[1], " \"");
return cmd; errfile = trim(outcmd[2], " \"");
return trim(outcmd[0]);
} }
} // namespace anon } // namespace anon
@ -223,10 +234,14 @@ string const parsecmd(string const & inputcmd, string & outfile)
int Systemcall::startscript(Starttype how, string const & what, int Systemcall::startscript(Starttype how, string const & what,
string const & path, bool process_events) string const & path, bool process_events)
{ {
string outfile; lyxerr << "\nRunning: " << what << endl;
QString cmd = QString::fromLocal8Bit(parsecmd(what, outfile).c_str());
SystemcallPrivate d(outfile); string outfile;
string errfile;
QString cmd = QString::fromLocal8Bit(
parsecmd(what, outfile, errfile).c_str());
SystemcallPrivate d(outfile, errfile);
d.startProcess(cmd, path); d.startProcess(cmd, path);
@ -259,17 +274,60 @@ int Systemcall::startscript(Starttype how, string const & what,
} }
SystemcallPrivate::SystemcallPrivate(const std::string& of) : SystemcallPrivate::SystemcallPrivate(std::string const & of,
process_(new QProcess), std::string const & ef) :
out_index_(0), process_(new QProcess),
err_index_(0), out_index_(0),
out_file_(of), err_index_(0),
process_events_(false) out_file_(of),
err_file_(ef),
process_events_(false)
{ {
if (!out_file_.empty()) { if (!out_file_.empty()) {
// Check whether we have to simply throw away the output. if (out_file_[0] == '&') {
if (out_file_ != os::nulldev()) if (subst(out_file_, " ", "") == "&2"
process_->setStandardOutputFile(QString::fromLocal8Bit(out_file_.c_str())); && err_file_[0] != '&') {
out_file_ = err_file_;
process_->setProcessChannelMode(
QProcess::MergedChannels);
} else {
if (err_file_[0] == '&') {
// Leave alone things such as
// "1>&2 2>&1". Should not be harmful,
// but let's give anyway a warning.
LYXERR0("Unsupported stdout/stderr redirect.");
err_file_.erase();
} else {
LYXERR0("Ambiguous stdout redirect: "
<< out_file_);
}
out_file_ = os::nulldev();
}
}
// Check whether we have to set the output file.
if (out_file_ != os::nulldev()) {
process_->setStandardOutputFile(QString::fromLocal8Bit(
out_file_.c_str()));
}
}
if (!err_file_.empty()) {
if (err_file_[0] == '&') {
if (subst(err_file_, " ", "") == "&1"
&& out_file_[0] != '&') {
process_->setProcessChannelMode(
QProcess::MergedChannels);
} else {
LYXERR0("Ambiguous stderr redirect: "
<< err_file_);
}
// In MergedChannels mode stderr goes to stdout.
err_file_ = os::nulldev();
}
// Check whether we have to set the error file.
if (err_file_ != os::nulldev()) {
process_->setStandardErrorFile(QString::fromLocal8Bit(
err_file_.c_str()));
}
} }
connect(process_, SIGNAL(readyReadStandardOutput()), SLOT(stdOut())); connect(process_, SIGNAL(readyReadStandardOutput()), SLOT(stdOut()));

View File

@ -32,7 +32,7 @@ class SystemcallPrivate : public QObject
Q_OBJECT Q_OBJECT
public: public:
SystemcallPrivate(std::string const & outfile); SystemcallPrivate(std::string const & outfile, std::string const & errfile);
~SystemcallPrivate(); ~SystemcallPrivate();
enum State { enum State {
@ -74,6 +74,8 @@ private:
size_t err_index_; size_t err_index_;
/// ///
std::string out_file_; std::string out_file_;
///
std::string err_file_;
/// Size of buffers. /// Size of buffers.
static size_t const buffer_size_ = 200; static size_t const buffer_size_ = 200;

View File

@ -39,6 +39,7 @@ What's new
Note that this mechanism triggers only if the binary is invoked exactly Note that this mechanism triggers only if the binary is invoked exactly
as "python -tt", which is the way it is done internally. as "python -tt", which is the way it is done internally.
- Allow redirecting stderr in converters (other than stdout).
* TEX2LYX IMPROVEMENTS * TEX2LYX IMPROVEMENTS