templater: strip single backslash before quotation mark in quoted template
authorYuya Nishihara <yuya@tcha.org>
Fri, 08 May 2015 18:11:26 +0900
changeset 24966 554d6fcc3c84
parent 24965 cecbe207cebd
child 24967 00790cc2b753
templater: strip single backslash before quotation mark in quoted template db7463aa080f fixed the issue of double escapes, but it made the following template fail with syntax error because of <\">. Strictly speaking, <\"> appears to be invalid in non-string part, but we are likely to escape <"> if surrounded by quotes, and we are used to write such templates by trial and error. [templates] sl = "{tags % \"{ifeq(tag,'tip','',label('log.tag', ' {tag}'))}\"}" So, for backward compatibility between 2.8.1 and 3.4, a single backslash before quotation mark is stripped only in quoted template. We don't care for <\"> in string literal in quoted template, which never worked as expected before. template result --------- ------------------------ {\"\"} parse error "{""}" {""} -> <> "{\"\"}" {""} -> <> {"\""} {"\""} -> <"> '{"\""}' {"\""} -> <"> "{"\""}" parse error (don't care)
mercurial/templater.py
tests/test-command-template.t
--- a/mercurial/templater.py	Thu May 07 23:18:48 2015 -0700
+++ b/mercurial/templater.py	Fri May 08 18:11:26 2015 +0900
@@ -623,7 +623,21 @@
     if quoted:
         if len(s) < 2 or s[0] != s[-1]:
             raise SyntaxError(_('unmatched quotes'))
-        return s[1:-1]
+        # de-backslash-ify only <\">. it is invalid syntax in non-string part of
+        # template, but we are likely to escape <"> in quoted string and it was
+        # accepted before, thanks to issue4290. <\\"> is unmodified because it
+        # is ambiguous and it was processed as such before 2.8.1.
+        #
+        #  template  result
+        #  --------- ------------------------
+        #  {\"\"}    parse error
+        #  "{""}"    {""} -> <>
+        #  "{\"\"}"  {""} -> <>
+        #  {"\""}    {"\""} -> <">
+        #  '{"\""}'  {"\""} -> <">
+        #  "{"\""}"  parse error (don't care)
+        q = s[0]
+        return s[1:-1].replace('\\\\' + q, '\\\\\\' + q).replace('\\' + q, q)
 
     return s
 
--- a/tests/test-command-template.t	Thu May 07 23:18:48 2015 -0700
+++ b/tests/test-command-template.t	Fri May 08 18:11:26 2015 +0900
@@ -2313,6 +2313,25 @@
   <>\n<]>
   <>\n<
 
+Test exception in quoted template. single backslash before quotation mark is
+stripped before parsing:
+
+  $ cat <<'EOF' > escquotetmpl
+  > changeset = "\" \\" \\\" \\\\" {files % \"{file}\"}\n"
+  > EOF
+  $ cd latesttag
+  $ hg log -r 2 --style ../escquotetmpl
+  " \" \" \\" head1
+
+  $ hg log -r 2 -T esc --config templates.esc='{\"invalid\"}\n'
+  hg: parse error at 1: syntax error
+  [255]
+  $ hg log -r 2 -T esc --config templates.esc='"{\"valid\"}\n"'
+  valid
+  $ hg log -r 2 -T esc --config templates.esc="'"'{\'"'"'valid\'"'"'}\n'"'"
+  valid
+  $ cd ..
+
 Test leading backslashes:
 
   $ cd latesttag