Really fix bug #4468.

The old fix was incomplete (\verb~\~ was translated to \verb~~ in roundtrip).
The real cause for this bug (and also the mistranslation of \href{...}{\}})
was the misbehaviour of Token::character() (see comment in Parser.h): This
method even returns a character if the category is catEscape, and this is not
wanted in most (all?) cases.
This commit is contained in:
Georg Baum 2012-10-05 00:12:18 +02:00
parent 5afe35cc59
commit 2f7f0c7631
7 changed files with 73 additions and 26 deletions

View File

@ -383,7 +383,7 @@ bool Parser::hasOpt()
} }
Parser::Arg Parser::getFullArg(char left, char right) Parser::Arg Parser::getFullArg(char left, char right, bool allow_escaping)
{ {
skip_spaces(true); skip_spaces(true);
@ -393,36 +393,40 @@ Parser::Arg Parser::getFullArg(char left, char right)
return make_pair(false, string()); return make_pair(false, string());
string result; string result;
char c = getChar(); Token t = get_token();
if (c != left) { if (t.cat() == catComment || t.cat() == catEscape ||
t.character() != left) {
putback(); putback();
return make_pair(false, string()); return make_pair(false, string());
} else { } else {
// a single '\' is only allowed within \verb, no matter what the delimiter is, for (t = get_token(); good(); t = get_token()) {
// for example "\verb+\+" (reported as bug #4468)
// To support this, we allow single '\' if it is the only character
// within equal delimiters
if (next_token().cat() == catEscape)
if (next_token().character() == right && right == left)
result += '\\';
while ((c = getChar()) != right && good()) {
// Ignore comments // Ignore comments
if (curr_token().cat() == catComment) { if (t.cat() == catComment) {
if (!curr_token().cs().empty()) if (!t.cs().empty())
cerr << "Ignoring comment: " << curr_token().asInput(); cerr << "Ignoring comment: " << t.asInput();
continue;
} }
else if (allow_escaping) {
result += curr_token().asInput(); if (t.cat() != catEscape && t.character() == right)
break;
} else {
if (t.character() == right) {
if (t.cat() == catEscape)
result += '\\';
break;
}
}
result += t.asInput();
} }
} }
return make_pair(true, result); return make_pair(true, result);
} }
string Parser::getArg(char left, char right) string Parser::getArg(char left, char right, bool allow_escaping)
{ {
return getFullArg(left, right).second; return getFullArg(left, right, allow_escaping).second;
} }

View File

@ -87,8 +87,8 @@ public:
* ../mathed/MathParser.cpp (which is the anchestor of this * ../mathed/MathParser.cpp (which is the anchestor of this
* class) uses a separate char member for this method. I * class) uses a separate char member for this method. I
* believe that the intended usage is to not cover tokens with * believe that the intended usage is to not cover tokens with
* catEscape, e.g. \code * catEscape or catComment, e.g. \code
* return (cs_.empty() || cat_ == catEscape) ? 0 : cs_[0]; * return (cs_.empty() || cat_ == catEscape || cat_ == catComment) ? 0 : cs_[0];
* \endcode * \endcode
* All usages of this method should be checked. gb 2011-01-05 * All usages of this method should be checked. gb 2011-01-05
*/ */
@ -157,18 +157,24 @@ public:
typedef std::pair<bool, std::string> Arg; typedef std::pair<bool, std::string> Arg;
/*! /*!
* Get an argument enclosed by \p left and \p right. * Get an argument enclosed by \p left and \p right.
* If \p allow_escaping is true, a right delimiter escaped by a
* backslash does not count as delimiter, but is included in the
* argument.
* \returns wether an argument was found in \p Arg.first and the * \returns wether an argument was found in \p Arg.first and the
* argument in \p Arg.second. \see getArg(). * argument in \p Arg.second. \see getArg().
*/ */
Arg getFullArg(char left, char right); Arg getFullArg(char left, char right, bool allow_escaping = true);
/*! /*!
* Get an argument enclosed by \p left and \p right. * Get an argument enclosed by \p left and \p right.
* If \p allow_escaping is true, a right delimiter escaped by a
* backslash does not count as delimiter, but is included in the
* argument.
* \returns the argument (without \p left and \p right) or the empty * \returns the argument (without \p left and \p right) or the empty
* string if the next non-space token is not \p left. Use * string if the next non-space token is not \p left. Use
* getFullArg() if you need to know wether there was an empty * getFullArg() if you need to know wether there was an empty
* argument or no argument at all. * argument or no argument at all.
*/ */
std::string getArg(char left, char right); std::string getArg(char left, char right, bool allow_escaping = true);
/*! /*!
* Like getOpt(), but distinguishes between a missing argument "" * Like getOpt(), but distinguishes between a missing argument ""
* and an empty argument "[]". * and an empty argument "[]".

View File

@ -968,6 +968,19 @@ target "http://www.test.test"
\end_inset \end_inset
\end_layout
\begin_layout Standard
parser test (escaped):
\begin_inset CommandInset href
LatexCommand href
name "a brace } and another one { and something"
target "http://www.test.test"
\end_inset
\end_layout \end_layout
\begin_layout Section \begin_layout Section

View File

@ -160,6 +160,8 @@ ftp2:\href{ftp://www.test.test}{www.test.test}
parser test (stupid, but valid):\href{http://www.test.test}{\}} parser test (stupid, but valid):\href{http://www.test.test}{\}}
parser test (escaped):\href{http://www.test.test}{a brace \} and another one \{ and something}
\section{Lists\index{Lists}} \section{Lists\index{Lists}}

View File

@ -1119,7 +1119,9 @@ status collapsed
\backslash \backslash
verb~~ verb~
\backslash
~
\end_layout \end_layout
\end_inset \end_inset
@ -1140,6 +1142,21 @@ item[ABC] first item+
\end_inset \end_inset
\begin_inset ERT
status collapsed
\begin_layout Standard
\backslash
verb+something
\backslash
+
\end_layout
\end_inset
bug 4468
\end_layout \end_layout
\begin_layout Standard \begin_layout Standard

View File

@ -321,6 +321,7 @@ zzz \section{
\end{verbatim} \end{verbatim}
\verb~\~ \verb~\~
\verb+\item[ABC] first item+ \verb+\item[ABC] first item+
\verb+something\+ bug 4468
and bibliography: and bibliography:
\begin{thebibliography}{9} \begin{thebibliography}{9}

View File

@ -3210,8 +3210,8 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
else if (t.cs() == "href") { else if (t.cs() == "href") {
context.check_layout(os); context.check_layout(os);
string target = p.getArg('{', '}'); string target = convert_command_inset_arg(p.verbatim_item());
string name = p.getArg('{', '}'); string name = convert_command_inset_arg(p.verbatim_item());
string type; string type;
size_t i = target.find(':'); size_t i = target.find(':');
if (i != string::npos) { if (i != string::npos) {
@ -3729,7 +3729,11 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
else if (t.cs() == "verb") { else if (t.cs() == "verb") {
context.check_layout(os); context.check_layout(os);
char const delimiter = p.next_token().character(); char const delimiter = p.next_token().character();
string const arg = p.getArg(delimiter, delimiter); // \verb is special: The usual escaping rules do not
// apply, e.g. "\verb+\+" is valid and denotes a single
// backslash (bug #4468). Therefore we do not allow
// escaping in getArg().
string const arg = p.getArg(delimiter, delimiter, false);
ostringstream oss; ostringstream oss;
oss << "\\verb" << delimiter << arg << delimiter; oss << "\\verb" << delimiter << arg << delimiter;
handle_ert(os, oss.str(), context); handle_ert(os, oss.str(), context);