Fix pasting as LaTeX in documents with no modules

When there is no module, the old code would invoke tex2lyx with option
 -m
instead of
 -m ""
and an error would ensue.

This is because the QProcess command line parser used in Systemcall is
broken and does not recognize empty parameters as such. The solution
is to rewrite our parsecmd() parser to generate a list of parameters.
This is post-2.4.0 work.

The workaround used here is:

- set the list of modules to "," when it should be empty. In effect,
  this is a list of two empty modules.

- change tex2lyx to accept empty module names and ignore them; this is
  good in terms of robustness anyway.

Additionally, when there is no receiving buffer, set the defaults as
the BufferParams defaults instead of empty (this is cleaner, but
should not make a difference in practice).

In the long term, we should switch to use the QStringList-based API of
QProcess in Systemcall (see QTBUG-80640).
This commit is contained in:
Jean-Marc Lasgouttes 2022-10-27 17:26:53 +02:00
parent 157d67d55d
commit 90ea508111
5 changed files with 30 additions and 13 deletions

View File

@ -3840,5 +3840,11 @@ string const BufferParams::bibFileEncoding(string const & file) const
} }
BufferParams const & defaultBufferParams()
{
static BufferParams default_params;
return default_params;
}
} // namespace lyx } // namespace lyx

View File

@ -691,6 +691,10 @@ private:
support::copied_ptr<Impl, MemoryTraits> pimpl_; support::copied_ptr<Impl, MemoryTraits> pimpl_;
}; };
///
BufferParams const & defaultBufferParams();
} // namespace lyx } // namespace lyx
#endif #endif

View File

@ -646,21 +646,24 @@ Converters::RetVal Converters::convert(Buffer const * buffer,
to_utf8(makeRelPath(from_utf8(outfile.absFileName()), from_utf8(path))); to_utf8(makeRelPath(from_utf8(outfile.absFileName()), from_utf8(path)));
string command = conv.command(); string command = conv.command();
BufferParams const & bparams = buffer ? buffer->params() : defaultBufferParams();
command = subst(command, token_from, quoteName(infile2)); command = subst(command, token_from, quoteName(infile2));
command = subst(command, token_base, quoteName(from_base)); command = subst(command, token_base, quoteName(from_base));
command = subst(command, token_to, quoteName(outfile2)); command = subst(command, token_to, quoteName(outfile2));
command = subst(command, token_path, quoteName(onlyPath(infile.absFileName()))); command = subst(command, token_path, quoteName(onlyPath(infile.absFileName())));
command = subst(command, token_orig_path, quoteName(onlyPath(orig_from.absFileName()))); command = subst(command, token_orig_path, quoteName(onlyPath(orig_from.absFileName())));
command = subst(command, token_orig_from, quoteName(onlyFileName(orig_from.absFileName()))); command = subst(command, token_orig_from, quoteName(onlyFileName(orig_from.absFileName())));
command = subst(command, token_textclass, command = subst(command, token_textclass, quoteName(bparams.documentClass().name()));
buffer ? quoteName(buffer->params().documentClass().name()) string modules = bparams.getModules().asString();
: string()); // FIXME: remove when SystemCall uses QProcess with the list API.
command = subst(command, token_modules, // Currently the QProcess parser is not able to encode an
buffer ? quoteName(buffer->params().getModules().asString()) // empty argument as ""; work around this by passing a
: string()); // single comma, that will be interpreted as a list of two
command = subst(command, token_encoding, // empty module names.
buffer ? quoteName(buffer->params().encoding().iconvName()) if (modules.empty())
: string()); modules = ",";
command = subst(command, token_modules, quoteName(modules));
command = subst(command, token_encoding, quoteName(bparams.encoding().iconvName()));
command = subst(command, token_python, os::python()); command = subst(command, token_python, os::python());
if (!conv.parselog().empty()) if (!conv.parselog().empty())

View File

@ -392,6 +392,11 @@ void SystemcallPrivate::startProcess(QString const & cmd, string const & path,
* The cleanest solution would be to have parsecmd() produce a * The cleanest solution would be to have parsecmd() produce a
* QStringList for arguments, instead of transforming the string * QStringList for arguments, instead of transforming the string
* into something that the QProcess splitter accepts. * into something that the QProcess splitter accepts.
*
* Another reason for doing that is that the Qt parser ignores
* empty "" arguments, which are needed in some instances (see
* e.g. the work around for modules in Converter:convert. See
* QTBUG-80640 for a discussion.
*/ */
QStringList arguments = QProcess::splitCommand(toqstr(latexEnvCmdPrefix(path, lpath)) + cmd_); QStringList arguments = QProcess::splitCommand(toqstr(latexEnvCmdPrefix(path, lpath)) + cmd_);
QString command = (arguments.empty()) ? QString() : arguments.first(); QString command = (arguments.empty()) ? QString() : arguments.first();

View File

@ -699,8 +699,6 @@ int parse_class(string const & arg, string const &)
int parse_module(string const & arg, string const &) int parse_module(string const & arg, string const &)
{ {
if (arg.empty())
error_message("Missing modules string after -m switch");
split(arg, preloaded_modules, ','); split(arg, preloaded_modules, ',');
return 1; return 1;
} }
@ -924,11 +922,12 @@ bool tex2lyx(idocstream & is, ostream & os, string const & encoding,
// Load preloaded modules. // Load preloaded modules.
// This needs to be done after the preamble is parsed, since the text // This needs to be done after the preamble is parsed, since the text
// class may not be known before. It neds to be done before parsing // class may not be known before. It needs to be done before parsing
// body, since otherwise the commands/environments provided by the // body, since otherwise the commands/environments provided by the
// modules would be parsed as ERT. // modules would be parsed as ERT.
// Empty module names are silently skipped.
for (auto const & module : preloaded_modules) { for (auto const & module : preloaded_modules) {
if (!addModule(module)) { if (!module.empty() && !addModule(module)) {
cerr << "Error: Could not load module \"" cerr << "Error: Could not load module \""
<< module << "\"." << endl; << module << "\"." << endl;
return false; return false;