aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/html
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2016-02-03 21:58:02 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2016-02-03 21:58:02 +0000
commitf98dd1a338867a408f7c72d73fbad7fe7fc93e3a (patch)
tree2f8da9862a9c1fe0df138917f997b03439c02773 /libgo/go/html
parentb081ed4efc144da0c45a6484aebfd10e0eb9fda3 (diff)
downloadgcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.zip
gcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.tar.gz
gcc-f98dd1a338867a408f7c72d73fbad7fe7fc93e3a.tar.bz2
libgo: Update to go1.6rc1.
Reviewed-on: https://go-review.googlesource.com/19200 From-SVN: r233110
Diffstat (limited to 'libgo/go/html')
-rw-r--r--libgo/go/html/escape.go60
-rw-r--r--libgo/go/html/escape_test.go20
-rw-r--r--libgo/go/html/template/clone_test.go12
-rw-r--r--libgo/go/html/template/context.go3
-rw-r--r--libgo/go/html/template/escape_test.go124
-rw-r--r--libgo/go/html/template/example_test.go37
-rw-r--r--libgo/go/html/template/template.go21
-rw-r--r--libgo/go/html/template/template_test.go29
-rw-r--r--libgo/go/html/template/transition.go2
9 files changed, 211 insertions, 97 deletions
diff --git a/libgo/go/html/escape.go b/libgo/go/html/escape.go
index f50a4b9..ab6fd1c 100644
--- a/libgo/go/html/escape.go
+++ b/libgo/go/html/escape.go
@@ -57,8 +57,9 @@ var replacementTable = [...]rune{
// unescapeEntity reads an entity like "&lt;" from b[src:] and writes the
// corresponding "<" to b[dst:], returning the incremented dst and src cursors.
// Precondition: b[src] == '&' && dst <= src.
-// attribute should be true if parsing an attribute value.
-func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
+func unescapeEntity(b []byte, dst, src int) (dst1, src1 int) {
+ const attribute = false
+
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
// i starts at 1 because we already know that s[0] == '&'.
@@ -139,14 +140,14 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
break
}
- entityName := string(s[1:i])
- if entityName == "" {
+ entityName := s[1:i]
+ if len(entityName) == 0 {
// No-op.
} else if attribute && entityName[len(entityName)-1] != ';' && len(s) > i && s[i] == '=' {
// No-op.
- } else if x := entity[entityName]; x != 0 {
+ } else if x := entity[string(entityName)]; x != 0 {
return dst + utf8.EncodeRune(b[dst:], x), src + i
- } else if x := entity2[entityName]; x[0] != 0 {
+ } else if x := entity2[string(entityName)]; x[0] != 0 {
dst1 := dst + utf8.EncodeRune(b[dst:], x[0])
return dst1 + utf8.EncodeRune(b[dst1:], x[1]), src + i
} else if !attribute {
@@ -155,7 +156,7 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
maxLen = longestEntityWithoutSemicolon
}
for j := maxLen; j > 1; j-- {
- if x := entity[entityName[:j]]; x != 0 {
+ if x := entity[string(entityName[:j])]; x != 0 {
return dst + utf8.EncodeRune(b[dst:], x), src + j + 1
}
}
@@ -166,26 +167,6 @@ func unescapeEntity(b []byte, dst, src int, attribute bool) (dst1, src1 int) {
return dst1, src1
}
-// unescape unescapes b's entities in-place, so that "a&lt;b" becomes "a<b".
-func unescape(b []byte) []byte {
- for i, c := range b {
- if c == '&' {
- dst, src := unescapeEntity(b, i, i, false)
- for src < len(b) {
- c := b[src]
- if c == '&' {
- dst, src = unescapeEntity(b, dst, src, false)
- } else {
- b[dst] = c
- dst, src = dst+1, src+1
- }
- }
- return b[0:dst]
- }
- }
- return b
-}
-
var htmlEscaper = strings.NewReplacer(
`&`, "&amp;",
`'`, "&#39;", // "&#39;" is shorter than "&apos;" and apos was not in HTML until HTML5.
@@ -208,8 +189,29 @@ func EscapeString(s string) string {
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func UnescapeString(s string) string {
- if !strings.Contains(s, "&") {
+ i := strings.IndexByte(s, '&')
+
+ if i < 0 {
return s
}
- return string(unescape([]byte(s)))
+
+ b := []byte(s)
+ dst, src := unescapeEntity(b, i, i)
+ for len(s[src:]) > 0 {
+ if s[src] == '&' {
+ i = 0
+ } else {
+ i = strings.IndexByte(s[src:], '&')
+ }
+ if i < 0 {
+ dst += copy(b[dst:], s[src:])
+ break
+ }
+
+ if i > 0 {
+ copy(b[dst:], s[src:src+i])
+ }
+ dst, src = unescapeEntity(b, dst+i, src+i)
+ }
+ return string(b[:dst])
}
diff --git a/libgo/go/html/escape_test.go b/libgo/go/html/escape_test.go
index 3702626..8b51a55 100644
--- a/libgo/go/html/escape_test.go
+++ b/libgo/go/html/escape_test.go
@@ -118,8 +118,10 @@ func TestUnescapeEscape(t *testing.T) {
}
var (
- benchEscapeData = strings.Repeat("AAAAA < BBBBB > CCCCC & DDDDD ' EEEEE \" ", 100)
- benchEscapeNone = strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 100)
+ benchEscapeData = strings.Repeat("AAAAA < BBBBB > CCCCC & DDDDD ' EEEEE \" ", 100)
+ benchEscapeNone = strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 100)
+ benchUnescapeSparse = strings.Repeat(strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 10)+"&amp;", 10)
+ benchUnescapeDense = strings.Repeat("&amp;&lt; &amp; &lt;", 100)
)
func BenchmarkEscape(b *testing.B) {
@@ -151,3 +153,17 @@ func BenchmarkUnescapeNone(b *testing.B) {
n += len(UnescapeString(s))
}
}
+
+func BenchmarkUnescapeSparse(b *testing.B) {
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(UnescapeString(benchUnescapeSparse))
+ }
+}
+
+func BenchmarkUnescapeDense(b *testing.B) {
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(UnescapeString(benchUnescapeDense))
+ }
+}
diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go
index c89d22a6..d7c62fa3 100644
--- a/libgo/go/html/template/clone_test.go
+++ b/libgo/go/html/template/clone_test.go
@@ -78,9 +78,17 @@ func TestClone(t *testing.T) {
Must(t0.Parse(`{{define "lhs"}} ( {{end}}`))
Must(t0.Parse(`{{define "rhs"}} ) {{end}}`))
- // Clone t0 as t4. Redefining the "lhs" template should fail.
+ // Clone t0 as t4. Redefining the "lhs" template should not fail.
t4 := Must(t0.Clone())
- if _, err := t4.Parse(`{{define "lhs"}} FAIL {{end}}`); err == nil {
+ if _, err := t4.Parse(`{{define "lhs"}} OK {{end}}`); err != nil {
+ t.Errorf(`redefine "lhs": got err %v want nil`, err)
+ }
+ // Cloning t1 should fail as it has been executed.
+ if _, err := t1.Clone(); err == nil {
+ t.Error("cloning t1: got nil err want non-nil")
+ }
+ // Redefining the "lhs" template in t1 should fail as it has been executed.
+ if _, err := t1.Parse(`{{define "lhs"}} OK {{end}}`); err == nil {
t.Error(`redefine "lhs": got nil err want non-nil`)
}
diff --git a/libgo/go/html/template/context.go b/libgo/go/html/template/context.go
index 59e794d..c90fc1f 100644
--- a/libgo/go/html/template/context.go
+++ b/libgo/go/html/template/context.go
@@ -310,7 +310,8 @@ func (e element) String() string {
return fmt.Sprintf("illegal element %d", int(e))
}
-// attr identifies the most recent HTML attribute when inside a start tag.
+// attr identifies the current HTML attribute when inside the attribute,
+// that is, starting from stateAttrName until stateTag/stateText (exclusive).
type attr uint8
const (
diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go
index bea2d13..707394e 100644
--- a/libgo/go/html/template/escape_test.go
+++ b/libgo/go/html/template/escape_test.go
@@ -1054,7 +1054,7 @@ func TestEscapeText(t *testing.T) {
},
{
`<a href=x`,
- context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href=x `,
@@ -1070,7 +1070,7 @@ func TestEscapeText(t *testing.T) {
},
{
`<a href ='`,
- context{state: stateURL, delim: delimSingleQuote},
+ context{state: stateURL, delim: delimSingleQuote, attr: attrURL},
},
{
`<a href=''`,
@@ -1078,7 +1078,7 @@ func TestEscapeText(t *testing.T) {
},
{
`<a href= "`,
- context{state: stateURL, delim: delimDoubleQuote},
+ context{state: stateURL, delim: delimDoubleQuote, attr: attrURL},
},
{
`<a href=""`,
@@ -1090,35 +1090,35 @@ func TestEscapeText(t *testing.T) {
},
{
`<a HREF='http:`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a Href='/`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href='"`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href="'`,
- context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href='&apos;`,
- context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimSingleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href="&quot;`,
- context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href="&#34;`,
- context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<a href=&quot;`,
- context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery},
+ context{state: stateURL, delim: delimSpaceOrTagEnd, urlPart: urlPartPreQuery, attr: attrURL},
},
{
`<img alt="1">`,
@@ -1138,83 +1138,83 @@ func TestEscapeText(t *testing.T) {
},
{
`<a onclick="`,
- context{state: stateJS, delim: delimDoubleQuote},
+ context{state: stateJS, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="//foo`,
- context{state: stateJSLineCmt, delim: delimDoubleQuote},
+ context{state: stateJSLineCmt, delim: delimDoubleQuote, attr: attrScript},
},
{
"<a onclick='//\n",
- context{state: stateJS, delim: delimSingleQuote},
+ context{state: stateJS, delim: delimSingleQuote, attr: attrScript},
},
{
"<a onclick='//\r\n",
- context{state: stateJS, delim: delimSingleQuote},
+ context{state: stateJS, delim: delimSingleQuote, attr: attrScript},
},
{
"<a onclick='//\u2028",
- context{state: stateJS, delim: delimSingleQuote},
+ context{state: stateJS, delim: delimSingleQuote, attr: attrScript},
},
{
`<a onclick="/*`,
- context{state: stateJSBlockCmt, delim: delimDoubleQuote},
+ context{state: stateJSBlockCmt, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="/*/`,
- context{state: stateJSBlockCmt, delim: delimDoubleQuote},
+ context{state: stateJSBlockCmt, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="/**/`,
- context{state: stateJS, delim: delimDoubleQuote},
+ context{state: stateJS, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onkeypress="&quot;`,
- context{state: stateJSDqStr, delim: delimDoubleQuote},
+ context{state: stateJSDqStr, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick='&quot;foo&quot;`,
- context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimSingleQuote, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<a onclick=&#39;foo&#39;`,
- context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimSpaceOrTagEnd, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<a onclick=&#39;foo`,
- context{state: stateJSSqStr, delim: delimSpaceOrTagEnd},
+ context{state: stateJSSqStr, delim: delimSpaceOrTagEnd, attr: attrScript},
},
{
`<a onclick="&quot;foo'`,
- context{state: stateJSDqStr, delim: delimDoubleQuote},
+ context{state: stateJSDqStr, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="'foo&quot;`,
- context{state: stateJSSqStr, delim: delimDoubleQuote},
+ context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
},
{
`<A ONCLICK="'`,
- context{state: stateJSSqStr, delim: delimDoubleQuote},
+ context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="/`,
- context{state: stateJSRegexp, delim: delimDoubleQuote},
+ context{state: stateJSRegexp, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="'foo'`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<a onclick="'foo\'`,
- context{state: stateJSSqStr, delim: delimDoubleQuote},
+ context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="'foo\'`,
- context{state: stateJSSqStr, delim: delimDoubleQuote},
+ context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="/foo/`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<script>/foo/ /=`,
@@ -1222,111 +1222,111 @@ func TestEscapeText(t *testing.T) {
},
{
`<a onclick="1 /foo`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<a onclick="1 /*c*/ /foo`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<a onclick="/foo[/]`,
- context{state: stateJSRegexp, delim: delimDoubleQuote},
+ context{state: stateJSRegexp, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="/foo\/`,
- context{state: stateJSRegexp, delim: delimDoubleQuote},
+ context{state: stateJSRegexp, delim: delimDoubleQuote, attr: attrScript},
},
{
`<a onclick="/foo/`,
- context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp},
+ context{state: stateJS, delim: delimDoubleQuote, jsCtx: jsCtxDivOp, attr: attrScript},
},
{
`<input checked style="`,
- context{state: stateCSS, delim: delimDoubleQuote},
+ context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="//`,
- context{state: stateCSSLineCmt, delim: delimDoubleQuote},
+ context{state: stateCSSLineCmt, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="//</script>`,
- context{state: stateCSSLineCmt, delim: delimDoubleQuote},
+ context{state: stateCSSLineCmt, delim: delimDoubleQuote, attr: attrStyle},
},
{
"<a style='//\n",
- context{state: stateCSS, delim: delimSingleQuote},
+ context{state: stateCSS, delim: delimSingleQuote, attr: attrStyle},
},
{
"<a style='//\r",
- context{state: stateCSS, delim: delimSingleQuote},
+ context{state: stateCSS, delim: delimSingleQuote, attr: attrStyle},
},
{
`<a style="/*`,
- context{state: stateCSSBlockCmt, delim: delimDoubleQuote},
+ context{state: stateCSSBlockCmt, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="/*/`,
- context{state: stateCSSBlockCmt, delim: delimDoubleQuote},
+ context{state: stateCSSBlockCmt, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="/**/`,
- context{state: stateCSS, delim: delimDoubleQuote},
+ context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="background: '`,
- context{state: stateCSSSqStr, delim: delimDoubleQuote},
+ context{state: stateCSSSqStr, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="background: &quot;`,
- context{state: stateCSSDqStr, delim: delimDoubleQuote},
+ context{state: stateCSSDqStr, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="background: '/foo?img=`,
- context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag},
+ context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag, attr: attrStyle},
},
{
`<a style="background: '/`,
- context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateCSSSqStr, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle},
},
{
`<a style="background: url(&#x22;/`,
- context{state: stateCSSDqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateCSSDqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle},
},
{
`<a style="background: url('/`,
- context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle},
},
{
`<a style="background: url('/)`,
- context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle},
},
{
`<a style="background: url('/ `,
- context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateCSSSqURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle},
},
{
`<a style="background: url(/`,
- context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery},
+ context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartPreQuery, attr: attrStyle},
},
{
`<a style="background: url( `,
- context{state: stateCSSURL, delim: delimDoubleQuote},
+ context{state: stateCSSURL, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="background: url( /image?name=`,
- context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag},
+ context{state: stateCSSURL, delim: delimDoubleQuote, urlPart: urlPartQueryOrFrag, attr: attrStyle},
},
{
`<a style="background: url(x)`,
- context{state: stateCSS, delim: delimDoubleQuote},
+ context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="background: url('x'`,
- context{state: stateCSS, delim: delimDoubleQuote},
+ context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<a style="background: url( x `,
- context{state: stateCSS, delim: delimDoubleQuote},
+ context{state: stateCSS, delim: delimDoubleQuote, attr: attrStyle},
},
{
`<!-- foo`,
@@ -1466,7 +1466,7 @@ func TestEscapeText(t *testing.T) {
},
{
`<a svg:style='`,
- context{state: stateCSS, delim: delimSingleQuote},
+ context{state: stateCSS, delim: delimSingleQuote, attr: attrStyle},
},
{
`<svg:font-face`,
@@ -1474,7 +1474,11 @@ func TestEscapeText(t *testing.T) {
},
{
`<svg:a svg:onclick="`,
- context{state: stateJS, delim: delimDoubleQuote},
+ context{state: stateJS, delim: delimDoubleQuote, attr: attrScript},
+ },
+ {
+ `<svg:a svg:onclick="x()">`,
+ context{},
},
}
diff --git a/libgo/go/html/template/example_test.go b/libgo/go/html/template/example_test.go
index 3ea3dca..de7cdbb 100644
--- a/libgo/go/html/template/example_test.go
+++ b/libgo/go/html/template/example_test.go
@@ -11,6 +11,7 @@ import (
"html/template"
"log"
"os"
+ "strings"
)
func Example() {
@@ -32,6 +33,7 @@ func Example() {
}
}
t, err := template.New("webpage").Parse(tpl)
+ check(err)
data := struct {
Title string
@@ -122,3 +124,38 @@ func Example_escape() {
// %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E
}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+func ExampleTemplate_block() {
+ const (
+ master = `Names:{{block "list" .}}{{"\n"}}{{range .}}{{println "-" .}}{{end}}{{end}}`
+ overlay = `{{define "list"}} {{join . ", "}}{{end}} `
+ )
+ var (
+ funcs = template.FuncMap{"join": strings.Join}
+ guardians = []string{"Gamora", "Groot", "Nebula", "Rocket", "Star-Lord"}
+ )
+ masterTmpl, err := template.New("master").Funcs(funcs).Parse(master)
+ if err != nil {
+ log.Fatal(err)
+ }
+ overlayTmpl, err := template.Must(masterTmpl.Clone()).Parse(overlay)
+ if err != nil {
+ log.Fatal(err)
+ }
+ if err := masterTmpl.Execute(os.Stdout, guardians); err != nil {
+ log.Fatal(err)
+ }
+ if err := overlayTmpl.Execute(os.Stdout, guardians); err != nil {
+ log.Fatal(err)
+ }
+ // Output:
+ // Names:
+ // - Gamora
+ // - Groot
+ // - Nebula
+ // - Rocket
+ // - Star-Lord
+ // Names: Gamora, Groot, Nebula, Rocket, Star-Lord
+}
diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go
index bb9140a..96ab268 100644
--- a/libgo/go/html/template/template.go
+++ b/libgo/go/html/template/template.go
@@ -17,7 +17,7 @@ import (
// Template is a specialized Template from "text/template" that produces a safe
// HTML document fragment.
type Template struct {
- // Sticky error if escaping fails.
+ // Sticky error if escaping fails, or escapeOK if succeeded.
escapeErr error
// We could embed the text/template field, but it's safer not to because
// we need to keep our version of the name space and the underlying
@@ -80,7 +80,7 @@ func (t *Template) escape() error {
defer t.nameSpace.mu.Unlock()
if t.escapeErr == nil {
if t.Tree == nil {
- return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.text.DefinedTemplates())
+ return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.DefinedTemplates())
}
if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
return err
@@ -143,6 +143,13 @@ func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err err
return tmpl, err
}
+// DefinedTemplates returns a string listing the defined templates,
+// prefixed by the string "; defined templates are: ". If there are none,
+// it returns the empty string. Used to generate an error message.
+func (t *Template) DefinedTemplates() string {
+ return t.text.DefinedTemplates()
+}
+
// Parse parses a string into a template. Nested template definitions
// will be associated with the top-level template t. Parse may be
// called multiple times to parse definitions of templates to associate
@@ -169,6 +176,8 @@ func (t *Template) Parse(src string) (*Template, error) {
tmpl := t.set[name]
if tmpl == nil {
tmpl = t.new(name)
+ } else if tmpl.escapeErr != nil {
+ return nil, fmt.Errorf("html/template: cannot redefine %q after it has executed", name)
}
// Restore our record of this text/template to its unescaped original state.
tmpl.escapeErr = nil
@@ -228,6 +237,7 @@ func (t *Template) Clone() (*Template, error) {
set: make(map[string]*Template),
},
}
+ ret.set[ret.Name()] = ret
for _, x := range textClone.Templates() {
name := x.Name()
src := t.set[name]
@@ -413,3 +423,10 @@ func parseGlob(t *Template, pattern string) (*Template, error) {
}
return parseFiles(t, filenames...)
}
+
+// IsTrue reports whether the value is 'true', in the sense of not the zero of its type,
+// and whether the value has a meaningful truth value. This is the definition of
+// truth used by if and other such actions.
+func IsTrue(val interface{}) (truth, ok bool) {
+ return template.IsTrue(val)
+}
diff --git a/libgo/go/html/template/template_test.go b/libgo/go/html/template/template_test.go
new file mode 100644
index 0000000..6f70d67
--- /dev/null
+++ b/libgo/go/html/template/template_test.go
@@ -0,0 +1,29 @@
+package template
+
+import (
+ "bytes"
+ "testing"
+)
+
+func TestTemplateClone(t *testing.T) {
+ // https://golang.org/issue/12996
+ orig := New("name")
+ clone, err := orig.Clone()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(clone.Templates()) != len(orig.Templates()) {
+ t.Fatalf("Invalid lenth of t.Clone().Templates()")
+ }
+
+ const want = "stuff"
+ parsed := Must(clone.Parse(want))
+ var buf bytes.Buffer
+ err = parsed.Execute(&buf, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got := buf.String(); got != want {
+ t.Fatalf("got %q; want %q", got, want)
+ }
+}
diff --git a/libgo/go/html/template/transition.go b/libgo/go/html/template/transition.go
index d2e0287..aefe035 100644
--- a/libgo/go/html/template/transition.go
+++ b/libgo/go/html/template/transition.go
@@ -169,7 +169,7 @@ func tBeforeValue(c context, s []byte) (context, int) {
case '"':
delim, i = delimDoubleQuote, i+1
}
- c.state, c.delim, c.attr = attrStartStates[c.attr], delim, attrNone
+ c.state, c.delim = attrStartStates[c.attr], delim
return c, i
}