aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/html
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2016-07-22 18:15:38 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2016-07-22 18:15:38 +0000
commit22b955cca564a9a3a5b8c9d9dd1e295b7943c128 (patch)
treeabdbd898676e1f853fca2d7e031d105d7ebcf676 /libgo/go/html
parent9d04a3af4c6491536badf6bde9707c907e4d196b (diff)
downloadgcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.zip
gcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.tar.gz
gcc-22b955cca564a9a3a5b8c9d9dd1e295b7943c128.tar.bz2
libgo: update to go1.7rc3
Reviewed-on: https://go-review.googlesource.com/25150 From-SVN: r238662
Diffstat (limited to 'libgo/go/html')
-rw-r--r--libgo/go/html/escape.go6
-rw-r--r--libgo/go/html/template/content.go29
-rw-r--r--libgo/go/html/template/css.go4
-rw-r--r--libgo/go/html/template/doc.go2
-rw-r--r--libgo/go/html/template/error.go2
-rw-r--r--libgo/go/html/template/escape.go96
-rw-r--r--libgo/go/html/template/escape_test.go2
-rw-r--r--libgo/go/html/template/examplefiles_test.go228
-rw-r--r--libgo/go/html/template/template.go14
-rw-r--r--libgo/go/html/template/template_test.go2
-rw-r--r--libgo/go/html/template/url.go2
11 files changed, 327 insertions, 60 deletions
diff --git a/libgo/go/html/escape.go b/libgo/go/html/escape.go
index ab6fd1c..8dd1f4a 100644
--- a/libgo/go/html/escape.go
+++ b/libgo/go/html/escape.go
@@ -10,10 +10,6 @@ import (
"unicode/utf8"
)
-type writer interface {
- WriteString(string) (int, error)
-}
-
// These replacements permit compatibility with old numeric entities that
// assumed Windows-1252 encoding.
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
@@ -185,7 +181,7 @@ func EscapeString(s string) string {
// UnescapeString unescapes entities like "&lt;" to become "<". It unescapes a
// larger range of entities than EscapeString escapes. For example, "&aacute;"
-// unescapes to "á", as does "&#225;" and "&xE1;".
+// unescapes to "á", as does "&#225;" and "&#xE1;".
// UnescapeString(EscapeString(s)) == s always holds, but the converse isn't
// always true.
func UnescapeString(s string) string {
diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go
index 3715ed5..2e14bd1 100644
--- a/libgo/go/html/template/content.go
+++ b/libgo/go/html/template/content.go
@@ -18,16 +18,28 @@ type (
// 4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`.
// See http://www.w3.org/TR/css3-syntax/#parsing and
// https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style
+ //
+ // Use of this type presents a security risk:
+ // the encapsulated content should come from a trusted source,
+ // as it will be included verbatim in the template output.
CSS string
// HTML encapsulates a known safe HTML document fragment.
// It should not be used for HTML from a third-party, or HTML with
// unclosed tags or comments. The outputs of a sound HTML sanitizer
// and a template escaped by this package are fine for use with HTML.
+ //
+ // Use of this type presents a security risk:
+ // the encapsulated content should come from a trusted source,
+ // as it will be included verbatim in the template output.
HTML string
// HTMLAttr encapsulates an HTML attribute from a trusted source,
// for example, ` dir="ltr"`.
+ //
+ // Use of this type presents a security risk:
+ // the encapsulated content should come from a trusted source,
+ // as it will be included verbatim in the template output.
HTMLAttr string
// JS encapsulates a known safe EcmaScript5 Expression, for example,
@@ -37,6 +49,15 @@ type (
// statement/expression ambiguity as when passing an expression like
// "{ foo: bar() }\n['foo']()", which is both a valid Expression and a
// valid Program with a very different meaning.
+ //
+ // Use of this type presents a security risk:
+ // the encapsulated content should come from a trusted source,
+ // as it will be included verbatim in the template output.
+ //
+ // Using JS to include valid but untrusted JSON is not safe.
+ // A safe alternative is to parse the JSON with json.Unmarshal and then
+ // pass the resultant object into the template, where it will be
+ // converted to sanitized JSON when presented in a JavaScript context.
JS string
// JSStr encapsulates a sequence of characters meant to be embedded
@@ -46,6 +67,10 @@ type (
// | EscapeSequence
// Note that LineContinuations are not allowed.
// JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not.
+ //
+ // Use of this type presents a security risk:
+ // the encapsulated content should come from a trusted source,
+ // as it will be included verbatim in the template output.
JSStr string
// URL encapsulates a known safe URL or URL substring (see RFC 3986).
@@ -53,6 +78,10 @@ type (
// from a trusted source should go in the page, but by default dynamic
// `javascript:` URLs are filtered out since they are a frequently
// exploited injection vector.
+ //
+ // Use of this type presents a security risk:
+ // the encapsulated content should come from a trusted source,
+ // as it will be included verbatim in the template output.
URL string
)
diff --git a/libgo/go/html/template/css.go b/libgo/go/html/template/css.go
index 3184648..9154d86 100644
--- a/libgo/go/html/template/css.go
+++ b/libgo/go/html/template/css.go
@@ -243,13 +243,13 @@ func cssValueFilter(args ...interface{}) string {
return filterFailsafe
}
default:
- if c < 0x80 && isCSSNmchar(rune(c)) {
+ if c < utf8.RuneSelf && isCSSNmchar(rune(c)) {
id = append(id, c)
}
}
}
id = bytes.ToLower(id)
- if bytes.Index(id, expressionBytes) != -1 || bytes.Index(id, mozBindingBytes) != -1 {
+ if bytes.Contains(id, expressionBytes) || bytes.Contains(id, mozBindingBytes) {
return filterFailsafe
}
return string(b)
diff --git a/libgo/go/html/template/doc.go b/libgo/go/html/template/doc.go
index 1827403..e1e9cad 100644
--- a/libgo/go/html/template/doc.go
+++ b/libgo/go/html/template/doc.go
@@ -166,7 +166,7 @@ that would have been produced if {{.}} was a regular string.
Security Model
-http://js-quasis-libraries-and-repl.googlecode.com/svn/trunk/safetemplate.html#problem_definition defines "safe" as used by this package.
+https://rawgit.com/mikesamuel/sanitized-jquery-templates/trunk/safetemplate.html#problem_definition defines "safe" as used by this package.
This package assumes that template authors are trusted, that Execute's data
parameter is not, and seeks to preserve the properties below in the face
diff --git a/libgo/go/html/template/error.go b/libgo/go/html/template/error.go
index 8f99e1b..5637384 100644
--- a/libgo/go/html/template/error.go
+++ b/libgo/go/html/template/error.go
@@ -164,7 +164,7 @@ const (
// different context than an earlier pass, there is no single context.
// In the example, there is missing a quote, so it is not clear
// whether {{.}} is meant to be inside a JS string or in a JS value
- // context. The second iteration would produce something like
+ // context. The second iteration would produce something like
//
// <script>var x = ['firstValue,'secondValue]</script>
ErrRangeLoopReentry
diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go
index 3c18340..8f2fe46 100644
--- a/libgo/go/html/template/escape.go
+++ b/libgo/go/html/template/escape.go
@@ -15,8 +15,8 @@ import (
// escapeTemplate rewrites the named template, which must be
// associated with t, to guarantee that the output of any of the named
-// templates is properly escaped. If no error is returned, then the named templates have
-// been modified. Otherwise the named templates have been rendered
+// templates is properly escaped. If no error is returned, then the named templates have
+// been modified. Otherwise the named templates have been rendered
// unusable.
func escapeTemplate(tmpl *Template, node parse.Node, name string) error {
e := newEscaper(tmpl)
@@ -46,30 +46,30 @@ func escapeTemplate(tmpl *Template, node parse.Node, name string) error {
// funcMap maps command names to functions that render their inputs safe.
var funcMap = template.FuncMap{
- "html_template_attrescaper": attrEscaper,
- "html_template_commentescaper": commentEscaper,
- "html_template_cssescaper": cssEscaper,
- "html_template_cssvaluefilter": cssValueFilter,
- "html_template_htmlnamefilter": htmlNameFilter,
- "html_template_htmlescaper": htmlEscaper,
- "html_template_jsregexpescaper": jsRegexpEscaper,
- "html_template_jsstrescaper": jsStrEscaper,
- "html_template_jsvalescaper": jsValEscaper,
- "html_template_nospaceescaper": htmlNospaceEscaper,
- "html_template_rcdataescaper": rcdataEscaper,
- "html_template_urlescaper": urlEscaper,
- "html_template_urlfilter": urlFilter,
- "html_template_urlnormalizer": urlNormalizer,
+ "_html_template_attrescaper": attrEscaper,
+ "_html_template_commentescaper": commentEscaper,
+ "_html_template_cssescaper": cssEscaper,
+ "_html_template_cssvaluefilter": cssValueFilter,
+ "_html_template_htmlnamefilter": htmlNameFilter,
+ "_html_template_htmlescaper": htmlEscaper,
+ "_html_template_jsregexpescaper": jsRegexpEscaper,
+ "_html_template_jsstrescaper": jsStrEscaper,
+ "_html_template_jsvalescaper": jsValEscaper,
+ "_html_template_nospaceescaper": htmlNospaceEscaper,
+ "_html_template_rcdataescaper": rcdataEscaper,
+ "_html_template_urlescaper": urlEscaper,
+ "_html_template_urlfilter": urlFilter,
+ "_html_template_urlnormalizer": urlNormalizer,
}
// equivEscapers matches contextual escapers to equivalent template builtins.
var equivEscapers = map[string]string{
- "html_template_attrescaper": "html",
- "html_template_htmlescaper": "html",
- "html_template_nospaceescaper": "html",
- "html_template_rcdataescaper": "html",
- "html_template_urlescaper": "urlquery",
- "html_template_urlnormalizer": "urlquery",
+ "_html_template_attrescaper": "html",
+ "_html_template_htmlescaper": "html",
+ "_html_template_nospaceescaper": "html",
+ "_html_template_rcdataescaper": "html",
+ "_html_template_urlescaper": "urlquery",
+ "_html_template_urlnormalizer": "urlquery",
}
// escaper collects type inferences about templates and changes needed to make
@@ -147,17 +147,17 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL:
switch c.urlPart {
case urlPartNone:
- s = append(s, "html_template_urlfilter")
+ s = append(s, "_html_template_urlfilter")
fallthrough
case urlPartPreQuery:
switch c.state {
case stateCSSDqStr, stateCSSSqStr:
- s = append(s, "html_template_cssescaper")
+ s = append(s, "_html_template_cssescaper")
default:
- s = append(s, "html_template_urlnormalizer")
+ s = append(s, "_html_template_urlnormalizer")
}
case urlPartQueryOrFrag:
- s = append(s, "html_template_urlescaper")
+ s = append(s, "_html_template_urlescaper")
case urlPartUnknown:
return context{
state: stateError,
@@ -167,27 +167,27 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
panic(c.urlPart.String())
}
case stateJS:
- s = append(s, "html_template_jsvalescaper")
+ s = append(s, "_html_template_jsvalescaper")
// A slash after a value starts a div operator.
c.jsCtx = jsCtxDivOp
case stateJSDqStr, stateJSSqStr:
- s = append(s, "html_template_jsstrescaper")
+ s = append(s, "_html_template_jsstrescaper")
case stateJSRegexp:
- s = append(s, "html_template_jsregexpescaper")
+ s = append(s, "_html_template_jsregexpescaper")
case stateCSS:
- s = append(s, "html_template_cssvaluefilter")
+ s = append(s, "_html_template_cssvaluefilter")
case stateText:
- s = append(s, "html_template_htmlescaper")
+ s = append(s, "_html_template_htmlescaper")
case stateRCDATA:
- s = append(s, "html_template_rcdataescaper")
+ s = append(s, "_html_template_rcdataescaper")
case stateAttr:
// Handled below in delim check.
case stateAttrName, stateTag:
c.state = stateAttrName
- s = append(s, "html_template_htmlnamefilter")
+ s = append(s, "_html_template_htmlnamefilter")
default:
if isComment(c.state) {
- s = append(s, "html_template_commentescaper")
+ s = append(s, "_html_template_commentescaper")
} else {
panic("unexpected state " + c.state.String())
}
@@ -196,9 +196,9 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
case delimNone:
// No extra-escaping needed for raw text content.
case delimSpaceOrTagEnd:
- s = append(s, "html_template_nospaceescaper")
+ s = append(s, "_html_template_nospaceescaper")
default:
- s = append(s, "html_template_attrescaper")
+ s = append(s, "_html_template_attrescaper")
}
e.editActionNode(n, s)
return c
@@ -276,22 +276,22 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x)
// for all x.
var redundantFuncs = map[string]map[string]bool{
- "html_template_commentescaper": {
- "html_template_attrescaper": true,
- "html_template_nospaceescaper": true,
- "html_template_htmlescaper": true,
+ "_html_template_commentescaper": {
+ "_html_template_attrescaper": true,
+ "_html_template_nospaceescaper": true,
+ "_html_template_htmlescaper": true,
},
- "html_template_cssescaper": {
- "html_template_attrescaper": true,
+ "_html_template_cssescaper": {
+ "_html_template_attrescaper": true,
},
- "html_template_jsregexpescaper": {
- "html_template_attrescaper": true,
+ "_html_template_jsregexpescaper": {
+ "_html_template_attrescaper": true,
},
- "html_template_jsstrescaper": {
- "html_template_attrescaper": true,
+ "_html_template_jsstrescaper": {
+ "_html_template_attrescaper": true,
},
- "html_template_urlescaper": {
- "html_template_urlnormalizer": true,
+ "_html_template_urlescaper": {
+ "_html_template_urlnormalizer": true,
},
}
diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go
index 707394e..023ee57 100644
--- a/libgo/go/html/template/escape_test.go
+++ b/libgo/go/html/template/escape_test.go
@@ -990,7 +990,7 @@ func TestErrors(t *testing.T) {
}
continue
}
- if strings.Index(got, test.err) == -1 {
+ if !strings.Contains(got, test.err) {
t.Errorf("input=%q: error\n\t%q\ndoes not contain expected string\n\t%q", test.input, got, test.err)
continue
}
diff --git a/libgo/go/html/template/examplefiles_test.go b/libgo/go/html/template/examplefiles_test.go
new file mode 100644
index 0000000..ffca8d5
--- /dev/null
+++ b/libgo/go/html/template/examplefiles_test.go
@@ -0,0 +1,228 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+package template_test
+
+import (
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "text/template"
+)
+
+// templateFile defines the contents of a template to be stored in a file, for testing.
+type templateFile struct {
+ name string
+ contents string
+}
+
+func createTestDir(files []templateFile) string {
+ dir, err := ioutil.TempDir("", "template")
+ if err != nil {
+ log.Fatal(err)
+ }
+ for _, file := range files {
+ f, err := os.Create(filepath.Join(dir, file.name))
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer f.Close()
+ _, err = io.WriteString(f, file.contents)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ return dir
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// Here we demonstrate loading a set of templates from a directory.
+func ExampleTemplate_glob() {
+ // Here we create a temporary directory and populate it with our sample
+ // template definition files; usually the template files would already
+ // exist in some location known to the program.
+ dir := createTestDir([]templateFile{
+ // T0.tmpl is a plain template file that just invokes T1.
+ {"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
+ // T1.tmpl defines a template, T1 that invokes T2.
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+ // T2.tmpl defines a template T2.
+ {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+ })
+ // Clean up after the test; another quirk of running as an example.
+ defer os.RemoveAll(dir)
+
+ // pattern is the glob pattern used to find all the template files.
+ pattern := filepath.Join(dir, "*.tmpl")
+
+ // Here starts the example proper.
+ // T0.tmpl is the first name matched, so it becomes the starting template,
+ // the value returned by ParseGlob.
+ tmpl := template.Must(template.ParseGlob(pattern))
+
+ err := tmpl.Execute(os.Stdout, nil)
+ if err != nil {
+ log.Fatalf("template execution: %s", err)
+ }
+ // Output:
+ // T0 invokes T1: (T1 invokes T2: (This is T2))
+}
+
+// Here we demonstrate loading a set of templates from files in different directories
+func ExampleTemplate_parsefiles() {
+ // Here we create different temporary directories and populate them with our sample
+ // template definition files; usually the template files would already
+ // exist in some location known to the program.
+ dir1 := createTestDir([]templateFile{
+ // T1.tmpl is a plain template file that just invokes T2.
+ {"T1.tmpl", `T1 invokes T2: ({{template "T2"}})`},
+ })
+
+ dir2 := createTestDir([]templateFile{
+ // T2.tmpl defines a template T2.
+ {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+ })
+
+ // Clean up after the test; another quirk of running as an example.
+ defer func(dirs ...string) {
+ for _, dir := range dirs {
+ os.RemoveAll(dir)
+ }
+ }(dir1, dir2)
+
+ // Here starts the example proper.
+ // Let's just parse only dir1/T0 and dir2/T2
+ paths := []string{
+ filepath.Join(dir1, "T1.tmpl"),
+ filepath.Join(dir2, "T2.tmpl"),
+ }
+ tmpl := template.Must(template.ParseFiles(paths...))
+
+ err := tmpl.Execute(os.Stdout, nil)
+ if err != nil {
+ log.Fatalf("template execution: %s", err)
+ }
+ // Output:
+ // T1 invokes T2: (This is T2)
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// This example demonstrates one way to share some templates
+// and use them in different contexts. In this variant we add multiple driver
+// templates by hand to an existing bundle of templates.
+func ExampleTemplate_helpers() {
+ // Here we create a temporary directory and populate it with our sample
+ // template definition files; usually the template files would already
+ // exist in some location known to the program.
+ dir := createTestDir([]templateFile{
+ // T1.tmpl defines a template, T1 that invokes T2.
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+ // T2.tmpl defines a template T2.
+ {"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
+ })
+ // Clean up after the test; another quirk of running as an example.
+ defer os.RemoveAll(dir)
+
+ // pattern is the glob pattern used to find all the template files.
+ pattern := filepath.Join(dir, "*.tmpl")
+
+ // Here starts the example proper.
+ // Load the helpers.
+ templates := template.Must(template.ParseGlob(pattern))
+ // Add one driver template to the bunch; we do this with an explicit template definition.
+ _, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
+ if err != nil {
+ log.Fatal("parsing driver1: ", err)
+ }
+ // Add another driver template.
+ _, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
+ if err != nil {
+ log.Fatal("parsing driver2: ", err)
+ }
+ // We load all the templates before execution. This package does not require
+ // that behavior but html/template's escaping does, so it's a good habit.
+ err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
+ if err != nil {
+ log.Fatalf("driver1 execution: %s", err)
+ }
+ err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
+ if err != nil {
+ log.Fatalf("driver2 execution: %s", err)
+ }
+ // Output:
+ // Driver 1 calls T1: (T1 invokes T2: (This is T2))
+ // Driver 2 calls T2: (This is T2)
+}
+
+// The following example is duplicated in text/template; keep them in sync.
+
+// This example demonstrates how to use one group of driver
+// templates with distinct sets of helper templates.
+func ExampleTemplate_share() {
+ // Here we create a temporary directory and populate it with our sample
+ // template definition files; usually the template files would already
+ // exist in some location known to the program.
+ dir := createTestDir([]templateFile{
+ // T0.tmpl is a plain template file that just invokes T1.
+ {"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
+ // T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
+ {"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
+ })
+ // Clean up after the test; another quirk of running as an example.
+ defer os.RemoveAll(dir)
+
+ // pattern is the glob pattern used to find all the template files.
+ pattern := filepath.Join(dir, "*.tmpl")
+
+ // Here starts the example proper.
+ // Load the drivers.
+ drivers := template.Must(template.ParseGlob(pattern))
+
+ // We must define an implementation of the T2 template. First we clone
+ // the drivers, then add a definition of T2 to the template name space.
+
+ // 1. Clone the helper set to create a new name space from which to run them.
+ first, err := drivers.Clone()
+ if err != nil {
+ log.Fatal("cloning helpers: ", err)
+ }
+ // 2. Define T2, version A, and parse it.
+ _, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
+ if err != nil {
+ log.Fatal("parsing T2: ", err)
+ }
+
+ // Now repeat the whole thing, using a different version of T2.
+ // 1. Clone the drivers.
+ second, err := drivers.Clone()
+ if err != nil {
+ log.Fatal("cloning drivers: ", err)
+ }
+ // 2. Define T2, version B, and parse it.
+ _, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
+ if err != nil {
+ log.Fatal("parsing T2: ", err)
+ }
+
+ // Execute the templates in the reverse order to verify the
+ // first is unaffected by the second.
+ err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
+ if err != nil {
+ log.Fatalf("second execution: %s", err)
+ }
+ err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
+ if err != nil {
+ log.Fatalf("first: execution: %s", err)
+ }
+
+ // Output:
+ // T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
+ // T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
+}
diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go
index 96ab268..063e46d 100644
--- a/libgo/go/html/template/template.go
+++ b/libgo/go/html/template/template.go
@@ -346,6 +346,11 @@ func Must(t *Template, err error) *Template {
// the named files. The returned template's name will have the (base) name and
// (parsed) contents of the first file. There must be at least one file.
// If an error occurs, parsing stops and the returned *Template is nil.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
+// For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
+// named "foo", while "a/foo" is unavailable.
func ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(nil, filenames...)
}
@@ -353,6 +358,9 @@ func ParseFiles(filenames ...string) (*Template, error) {
// ParseFiles parses the named files and associates the resulting templates with
// t. If an error occurs, parsing stops and the returned template is nil;
// otherwise it is t. There must be at least one file.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
return parseFiles(t, filenames...)
}
@@ -399,6 +407,9 @@ func parseFiles(t *Template, filenames ...string) (*Template, error) {
// returned template will have the (base) name and (parsed) contents of the
// first file matched by the pattern. ParseGlob is equivalent to calling
// ParseFiles with the list of files matched by the pattern.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func ParseGlob(pattern string) (*Template, error) {
return parseGlob(nil, pattern)
}
@@ -408,6 +419,9 @@ func ParseGlob(pattern string) (*Template, error) {
// processed by filepath.Glob and must match at least one file. ParseGlob is
// equivalent to calling t.ParseFiles with the list of files matched by the
// pattern.
+//
+// When parsing multiple files with the same name in different directories,
+// the last one mentioned will be the one that results.
func (t *Template) ParseGlob(pattern string) (*Template, error) {
return parseGlob(t, pattern)
}
diff --git a/libgo/go/html/template/template_test.go b/libgo/go/html/template/template_test.go
index 6f70d67..46df1f8 100644
--- a/libgo/go/html/template/template_test.go
+++ b/libgo/go/html/template/template_test.go
@@ -13,7 +13,7 @@ func TestTemplateClone(t *testing.T) {
t.Fatal(err)
}
if len(clone.Templates()) != len(orig.Templates()) {
- t.Fatalf("Invalid lenth of t.Clone().Templates()")
+ t.Fatalf("Invalid length of t.Clone().Templates()")
}
const want = "stuff"
diff --git a/libgo/go/html/template/url.go b/libgo/go/html/template/url.go
index 2ca76bf..246bfd3 100644
--- a/libgo/go/html/template/url.go
+++ b/libgo/go/html/template/url.go
@@ -17,7 +17,7 @@ func urlFilter(args ...interface{}) string {
if t == contentTypeURL {
return s
}
- if i := strings.IndexRune(s, ':'); i >= 0 && strings.IndexRune(s[:i], '/') < 0 {
+ if i := strings.IndexRune(s, ':'); i >= 0 && !strings.ContainsRune(s[:i], '/') {
protocol := strings.ToLower(s[:i])
if protocol != "http" && protocol != "https" && protocol != "mailto" {
return "#" + filterFailsafe