diff --git a/src/support/Systemcall.cpp b/src/support/Systemcall.cpp index 5262da7a26..d339e11b80 100644 --- a/src/support/Systemcall.cpp +++ b/src/support/Systemcall.cpp @@ -13,14 +13,13 @@ #include -#include "support/Systemcall.h" -#include "support/SystemcallPrivate.h" - #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/SystemcallPrivate.h" #include "support/os.h" #include "support/ProgressInterface.h" @@ -164,20 +163,19 @@ namespace { * "\a" -> "\a" * "a\"b" -> "a"""b" */ -QStringList const parsecmd(string const & incmd, string & infile, 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 = os::python(); - QStringList arguments; vector outcmd(4); size_t start = 0; - if (prefixIs(incmd, python_call + ' ')) { - arguments << QString::fromLocal8Bit(trim(os::python()).c_str()); - start = python_call.length() + 1; + if (prefixIs(incmd, python_call)) { + outcmd[0] = os::python(); + start = python_call.length(); } for (size_t i = start, o = 0; i < incmd.length(); ++i) { @@ -196,12 +194,30 @@ QStringList const parsecmd(string const & incmd, string & infile, string & outfi outcmd[o] += c; continue; } - if (c == ' ' && !(in_double_quote || escaped)) { - if (o == 0) - arguments << QString::fromLocal8Bit(trim(outcmd[o]).c_str()); - o = 0; + if (c == '"') { + if (escaped) { + // Don't triple double-quotes for redirection + // files as these won't be parsed by QProcess + outcmd[o] += string(o ? "\"" : "\"\"\""); + escaped = false; + } else { + outcmd[o] += c; + in_double_quote = !in_double_quote; + } + } else if (c == '\\' && !escaped) { + escaped = true; + } else if (c == '>' && !(in_double_quote || escaped)) { + if (suffixIs(outcmd[o], " 2")) { + outcmd[o] = rtrim(outcmd[o], "2"); + o = 2; + } else { + if (suffixIs(outcmd[o], " 1")) + outcmd[o] = rtrim(outcmd[o], "1"); + o = 1; + } + } else if (c == '<' && !(in_double_quote || escaped)) { + o = 3; #if defined (USE_MACOSX_PACKAGING) - // FIXME!!!! } else if (o == 0 && i > 4 && c == ' ' && !(in_double_quote || escaped)) { // if a macOS app is detected with an additional argument // use open command as prefix to get it work @@ -216,28 +232,6 @@ QStringList const parsecmd(string const & incmd, string & infile, string & outfi } outcmd[o] += c; #endif - } else if (c == '"') { - if (escaped) { - outcmd[o] += c; - escaped = false; - } else - in_double_quote = !in_double_quote; - } else if (c == '\\' && !escaped) { - escaped = true; - } else if (c == '>' && o == 0 && !(in_double_quote || escaped)) { - if (outcmd[o] == "2") { - outcmd[o].clear(); - o = 2; - outcmd[o].clear(); - } else if (outcmd[o] == "1" || outcmd[o].empty()) { - outcmd[o].clear(); - o = 1; - outcmd[o].clear(); - } - } else if (c == '<' && o == 0 && outcmd[o].empty() && !(in_double_quote || escaped)) { - outcmd[o].clear(); - o = 3; - outcmd[o].clear(); } else { if (escaped && in_double_quote) outcmd[o] += '\\'; @@ -248,7 +242,7 @@ QStringList const parsecmd(string const & incmd, string & infile, string & outfi infile = trim(outcmd[3], " \""); outfile = trim(outcmd[1], " \""); errfile = trim(outcmd[2], " \""); - return arguments; + return trim(outcmd[0]); } } // namespace @@ -273,7 +267,8 @@ int Systemcall::startscript(Starttype how, string const & what, string infile; string outfile; string errfile; - QStringList const cmd = parsecmd(what_ss, infile, outfile, errfile); + QString const cmd = QString::fromLocal8Bit( + parsecmd(what_ss, infile, outfile, errfile).c_str()); SystemcallPrivate d(infile, outfile, errfile); bool do_events = process_events || how == WaitLoop; @@ -384,21 +379,27 @@ SystemcallPrivate::SystemcallPrivate(std::string const & sf, std::string const & } -void SystemcallPrivate::startProcess(string const & cmd, string const & path, +void SystemcallPrivate::startProcess(QString const & cmd, string const & path, string const & lpath, bool detached) { - cmd_ = toqstr(cmd); - - // Set the environment for LaTeX - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - for (auto const & v : latexEnvironment(path, lpath)) - latexenv.insert(toqstr(v.first), QString::fromLocal8Bit(v.second.c_str())); - process_->setProcessEnvironment(env); - - // Parse the command line - QStringList arguments = parsecmd(cmd); - QString command = arguments.empty() ? QString() : arguments.takeFirst(); - + cmd_ = cmd; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + // FIXME pass command and arguments separated in the first place + /* The versions of startDetached() and start() that accept a + * QStringList object exist since Qt4, but it is only in Qt 5.15 + * that splitCommand() was introduced and the plain versions of + * start/startDetached() have been deprecated. + * The cleanest solution would be to have parsecmd() produce a + * QStringList for arguments, instead of transforming the string + * into something that the QProcess splitter accepts. + */ + QStringList arguments = QProcess::splitCommand(toqstr(latexEnvCmdPrefix(path, lpath)) + cmd_); + QString command = (arguments.empty()) ? QString() : arguments.first(); + if (arguments.size() == 1) + arguments.clear(); + else if (!arguments.empty()) + arguments.removeFirst(); +#endif if (detached) { state = SystemcallPrivate::Running; #ifdef Q_OS_WIN32 @@ -410,8 +411,11 @@ void SystemcallPrivate::startProcess(string const & cmd, string const & path, if (err_file_.empty()) process_->setStandardErrorFile(QProcess::nullDevice()); #endif - +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) if (!QProcess::startDetached(command, arguments)) { +#else + if (!QProcess::startDetached(toqstr(latexEnvCmdPrefix(path, lpath)) + cmd_)) { +#endif state = SystemcallPrivate::Error; return; } @@ -419,7 +423,11 @@ void SystemcallPrivate::startProcess(string const & cmd, string const & path, delete released; } else if (process_) { state = SystemcallPrivate::Starting; +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) process_->start(command, arguments); +#else + process_->start(toqstr(latexEnvCmdPrefix(path, lpath)) + cmd_); +#endif } } diff --git a/src/support/SystemcallPrivate.h b/src/support/SystemcallPrivate.h index f9b27c3430..19771dfc73 100644 --- a/src/support/SystemcallPrivate.h +++ b/src/support/SystemcallPrivate.h @@ -44,7 +44,7 @@ public: State state; bool waitWhile(State, bool processEvents, int timeout = -1); - void startProcess(std::string const & cmd, std::string const & path, + void startProcess(QString const & cmd, std::string const & path, std::string const & lpath, bool detach); int exitCode(); diff --git a/src/support/filetools.cpp b/src/support/filetools.cpp index 1bff01460d..d554f477eb 100644 --- a/src/support/filetools.cpp +++ b/src/support/filetools.cpp @@ -737,13 +737,12 @@ string const replaceEnvironmentPath(string const & path) // Return a command prefix for setting the environment of the TeX engine. -map latexEnvironment(string const & path, string const & lpath) +string latexEnvCmdPrefix(string const & path, string const & lpath) { - map env; bool use_lpath = !(lpath.empty() || lpath == "." || lpath == "./"); if (path.empty() || (lyxrc.texinputs_prefix.empty() && !use_lpath)) - return env; + return string(); string texinputs_prefix = lyxrc.texinputs_prefix.empty() ? string() : os::latex_path_list( @@ -767,37 +766,30 @@ map latexEnvironment(string const & path, string const & lpath) texinputs_prefix.append(sep + abslpath); } - // NOTE: the dummy blank dirs are necessary to force the - // QProcess parser to quote the argument (see bug 9453) - string const dummy = (os::shell() == os::UNIX) ? string() : (" " + sep); - - env.insert({ "TEXINPUTS", "." + sep + dummy + texinputs_prefix + sep + texinputs }); - env.insert({ "BIBINPUTS", "." + sep + dummy + allother_prefix + sep + bibinputs }); - env.insert({ "BSTINPUTS", "." + sep + dummy + allother_prefix + sep + bstinputs }); - env.insert({ "TEXFONTS", "." + sep + dummy + allother_prefix + sep + texfonts }); - - return env; -} - - -// Return a command prefix for setting the environment of the TeX engine. -string latexEnvCmdPrefix(string const & path, string const & lpath) -{ - auto const env = latexEnvironment(path, lpath); - if (env.empty()) - return string(); - - string prefix; - if (os::shell() == os::UNIX) { - prefix = "env "; - for (auto const & v : latexEnvironment(path, lpath)) - prefix += v.first + "=" + v.second + " "; - } else { - prefix = "cmd /d /c "; - for (auto const & v : latexEnvironment(path, lpath)) - prefix += "set \"" + v.first + "=" + v.second + "\" & "; - } - return prefix; + if (os::shell() == os::UNIX) + return "env TEXINPUTS=\"." + sep + texinputs_prefix + + sep + texinputs + "\" " + + "BIBINPUTS=\"." + sep + allother_prefix + + sep + bibinputs + "\" " + + "BSTINPUTS=\"." + sep + allother_prefix + + sep + bstinputs + "\" " + + "TEXFONTS=\"." + sep + allother_prefix + + sep + texfonts + "\" "; + else + // NOTE: the dummy blank dirs are necessary to force the + // QProcess parser to quote the argument (see bug 9453) + return "cmd /d /c set \"TEXINPUTS=." + sep + " " + + sep + texinputs_prefix + + sep + texinputs + "\" & " + + "set \"BIBINPUTS=." + sep + " " + + sep + allother_prefix + + sep + bibinputs + "\" & " + + "set \"BSTINPUTS=." + sep + " " + + sep + allother_prefix + + sep + bstinputs + "\" & " + + "set \"TEXFONTS=." + sep + " " + + sep + allother_prefix + + sep + texfonts + "\" & "; } diff --git a/src/support/filetools.h b/src/support/filetools.h index a4ee8e3380..404dec210f 100644 --- a/src/support/filetools.h +++ b/src/support/filetools.h @@ -14,10 +14,9 @@ #include "support/docstring.h" -#include -#include -#include #include +#include +#include namespace lyx { namespace support { @@ -290,15 +289,6 @@ std::string const onlyFileName(std::string const & fname); */ std::string const replaceEnvironmentPath(std::string const & path); - -/** - Return a map to be used to set the environment of the TeX engine - with respect to the paths \p path and \p lpath. - */ -std::map -latexEnvironment(std::string const & path, std::string const & lpath); - - /** Return a string to be used as a prefix to a command for setting the environment of the TeX engine with respect to the paths \p path and \p lpath.