...

Source file src/html/template/template.go

Documentation: html/template

		 1  // Copyright 2011 The Go Authors. All rights reserved.
		 2  // Use of this source code is governed by a BSD-style
		 3  // license that can be found in the LICENSE file.
		 4  
		 5  package template
		 6  
		 7  import (
		 8  	"fmt"
		 9  	"io"
		10  	"io/fs"
		11  	"os"
		12  	"path"
		13  	"path/filepath"
		14  	"sync"
		15  	"text/template"
		16  	"text/template/parse"
		17  )
		18  
		19  // Template is a specialized Template from "text/template" that produces a safe
		20  // HTML document fragment.
		21  type Template struct {
		22  	// Sticky error if escaping fails, or escapeOK if succeeded.
		23  	escapeErr error
		24  	// We could embed the text/template field, but it's safer not to because
		25  	// we need to keep our version of the name space and the underlying
		26  	// template's in sync.
		27  	text *template.Template
		28  	// The underlying template's parse tree, updated to be HTML-safe.
		29  	Tree			 *parse.Tree
		30  	*nameSpace // common to all associated templates
		31  }
		32  
		33  // escapeOK is a sentinel value used to indicate valid escaping.
		34  var escapeOK = fmt.Errorf("template escaped correctly")
		35  
		36  // nameSpace is the data structure shared by all templates in an association.
		37  type nameSpace struct {
		38  	mu			sync.Mutex
		39  	set		 map[string]*Template
		40  	escaped bool
		41  	esc		 escaper
		42  }
		43  
		44  // Templates returns a slice of the templates associated with t, including t
		45  // itself.
		46  func (t *Template) Templates() []*Template {
		47  	ns := t.nameSpace
		48  	ns.mu.Lock()
		49  	defer ns.mu.Unlock()
		50  	// Return a slice so we don't expose the map.
		51  	m := make([]*Template, 0, len(ns.set))
		52  	for _, v := range ns.set {
		53  		m = append(m, v)
		54  	}
		55  	return m
		56  }
		57  
		58  // Option sets options for the template. Options are described by
		59  // strings, either a simple string or "key=value". There can be at
		60  // most one equals sign in an option string. If the option string
		61  // is unrecognized or otherwise invalid, Option panics.
		62  //
		63  // Known options:
		64  //
		65  // missingkey: Control the behavior during execution if a map is
		66  // indexed with a key that is not present in the map.
		67  //	"missingkey=default" or "missingkey=invalid"
		68  //		The default behavior: Do nothing and continue execution.
		69  //		If printed, the result of the index operation is the string
		70  //		"<no value>".
		71  //	"missingkey=zero"
		72  //		The operation returns the zero value for the map type's element.
		73  //	"missingkey=error"
		74  //		Execution stops immediately with an error.
		75  //
		76  func (t *Template) Option(opt ...string) *Template {
		77  	t.text.Option(opt...)
		78  	return t
		79  }
		80  
		81  // checkCanParse checks whether it is OK to parse templates.
		82  // If not, it returns an error.
		83  func (t *Template) checkCanParse() error {
		84  	if t == nil {
		85  		return nil
		86  	}
		87  	t.nameSpace.mu.Lock()
		88  	defer t.nameSpace.mu.Unlock()
		89  	if t.nameSpace.escaped {
		90  		return fmt.Errorf("html/template: cannot Parse after Execute")
		91  	}
		92  	return nil
		93  }
		94  
		95  // escape escapes all associated templates.
		96  func (t *Template) escape() error {
		97  	t.nameSpace.mu.Lock()
		98  	defer t.nameSpace.mu.Unlock()
		99  	t.nameSpace.escaped = true
	 100  	if t.escapeErr == nil {
	 101  		if t.Tree == nil {
	 102  			return fmt.Errorf("template: %q is an incomplete or empty template", t.Name())
	 103  		}
	 104  		if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
	 105  			return err
	 106  		}
	 107  	} else if t.escapeErr != escapeOK {
	 108  		return t.escapeErr
	 109  	}
	 110  	return nil
	 111  }
	 112  
	 113  // Execute applies a parsed template to the specified data object,
	 114  // writing the output to wr.
	 115  // If an error occurs executing the template or writing its output,
	 116  // execution stops, but partial results may already have been written to
	 117  // the output writer.
	 118  // A template may be executed safely in parallel, although if parallel
	 119  // executions share a Writer the output may be interleaved.
	 120  func (t *Template) Execute(wr io.Writer, data interface{}) error {
	 121  	if err := t.escape(); err != nil {
	 122  		return err
	 123  	}
	 124  	return t.text.Execute(wr, data)
	 125  }
	 126  
	 127  // ExecuteTemplate applies the template associated with t that has the given
	 128  // name to the specified data object and writes the output to wr.
	 129  // If an error occurs executing the template or writing its output,
	 130  // execution stops, but partial results may already have been written to
	 131  // the output writer.
	 132  // A template may be executed safely in parallel, although if parallel
	 133  // executions share a Writer the output may be interleaved.
	 134  func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
	 135  	tmpl, err := t.lookupAndEscapeTemplate(name)
	 136  	if err != nil {
	 137  		return err
	 138  	}
	 139  	return tmpl.text.Execute(wr, data)
	 140  }
	 141  
	 142  // lookupAndEscapeTemplate guarantees that the template with the given name
	 143  // is escaped, or returns an error if it cannot be. It returns the named
	 144  // template.
	 145  func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
	 146  	t.nameSpace.mu.Lock()
	 147  	defer t.nameSpace.mu.Unlock()
	 148  	t.nameSpace.escaped = true
	 149  	tmpl = t.set[name]
	 150  	if tmpl == nil {
	 151  		return nil, fmt.Errorf("html/template: %q is undefined", name)
	 152  	}
	 153  	if tmpl.escapeErr != nil && tmpl.escapeErr != escapeOK {
	 154  		return nil, tmpl.escapeErr
	 155  	}
	 156  	if tmpl.text.Tree == nil || tmpl.text.Root == nil {
	 157  		return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
	 158  	}
	 159  	if t.text.Lookup(name) == nil {
	 160  		panic("html/template internal error: template escaping out of sync")
	 161  	}
	 162  	if tmpl.escapeErr == nil {
	 163  		err = escapeTemplate(tmpl, tmpl.text.Root, name)
	 164  	}
	 165  	return tmpl, err
	 166  }
	 167  
	 168  // DefinedTemplates returns a string listing the defined templates,
	 169  // prefixed by the string "; defined templates are: ". If there are none,
	 170  // it returns the empty string. Used to generate an error message.
	 171  func (t *Template) DefinedTemplates() string {
	 172  	return t.text.DefinedTemplates()
	 173  }
	 174  
	 175  // Parse parses text as a template body for t.
	 176  // Named template definitions ({{define ...}} or {{block ...}} statements) in text
	 177  // define additional templates associated with t and are removed from the
	 178  // definition of t itself.
	 179  //
	 180  // Templates can be redefined in successive calls to Parse,
	 181  // before the first use of Execute on t or any associated template.
	 182  // A template definition with a body containing only white space and comments
	 183  // is considered empty and will not replace an existing template's body.
	 184  // This allows using Parse to add new named template definitions without
	 185  // overwriting the main template body.
	 186  func (t *Template) Parse(text string) (*Template, error) {
	 187  	if err := t.checkCanParse(); err != nil {
	 188  		return nil, err
	 189  	}
	 190  
	 191  	ret, err := t.text.Parse(text)
	 192  	if err != nil {
	 193  		return nil, err
	 194  	}
	 195  
	 196  	// In general, all the named templates might have changed underfoot.
	 197  	// Regardless, some new ones may have been defined.
	 198  	// The template.Template set has been updated; update ours.
	 199  	t.nameSpace.mu.Lock()
	 200  	defer t.nameSpace.mu.Unlock()
	 201  	for _, v := range ret.Templates() {
	 202  		name := v.Name()
	 203  		tmpl := t.set[name]
	 204  		if tmpl == nil {
	 205  			tmpl = t.new(name)
	 206  		}
	 207  		tmpl.text = v
	 208  		tmpl.Tree = v.Tree
	 209  	}
	 210  	return t, nil
	 211  }
	 212  
	 213  // AddParseTree creates a new template with the name and parse tree
	 214  // and associates it with t.
	 215  //
	 216  // It returns an error if t or any associated template has already been executed.
	 217  func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
	 218  	if err := t.checkCanParse(); err != nil {
	 219  		return nil, err
	 220  	}
	 221  
	 222  	t.nameSpace.mu.Lock()
	 223  	defer t.nameSpace.mu.Unlock()
	 224  	text, err := t.text.AddParseTree(name, tree)
	 225  	if err != nil {
	 226  		return nil, err
	 227  	}
	 228  	ret := &Template{
	 229  		nil,
	 230  		text,
	 231  		text.Tree,
	 232  		t.nameSpace,
	 233  	}
	 234  	t.set[name] = ret
	 235  	return ret, nil
	 236  }
	 237  
	 238  // Clone returns a duplicate of the template, including all associated
	 239  // templates. The actual representation is not copied, but the name space of
	 240  // associated templates is, so further calls to Parse in the copy will add
	 241  // templates to the copy but not to the original. Clone can be used to prepare
	 242  // common templates and use them with variant definitions for other templates
	 243  // by adding the variants after the clone is made.
	 244  //
	 245  // It returns an error if t has already been executed.
	 246  func (t *Template) Clone() (*Template, error) {
	 247  	t.nameSpace.mu.Lock()
	 248  	defer t.nameSpace.mu.Unlock()
	 249  	if t.escapeErr != nil {
	 250  		return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
	 251  	}
	 252  	textClone, err := t.text.Clone()
	 253  	if err != nil {
	 254  		return nil, err
	 255  	}
	 256  	ns := &nameSpace{set: make(map[string]*Template)}
	 257  	ns.esc = makeEscaper(ns)
	 258  	ret := &Template{
	 259  		nil,
	 260  		textClone,
	 261  		textClone.Tree,
	 262  		ns,
	 263  	}
	 264  	ret.set[ret.Name()] = ret
	 265  	for _, x := range textClone.Templates() {
	 266  		name := x.Name()
	 267  		src := t.set[name]
	 268  		if src == nil || src.escapeErr != nil {
	 269  			return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
	 270  		}
	 271  		x.Tree = x.Tree.Copy()
	 272  		ret.set[name] = &Template{
	 273  			nil,
	 274  			x,
	 275  			x.Tree,
	 276  			ret.nameSpace,
	 277  		}
	 278  	}
	 279  	// Return the template associated with the name of this template.
	 280  	return ret.set[ret.Name()], nil
	 281  }
	 282  
	 283  // New allocates a new HTML template with the given name.
	 284  func New(name string) *Template {
	 285  	ns := &nameSpace{set: make(map[string]*Template)}
	 286  	ns.esc = makeEscaper(ns)
	 287  	tmpl := &Template{
	 288  		nil,
	 289  		template.New(name),
	 290  		nil,
	 291  		ns,
	 292  	}
	 293  	tmpl.set[name] = tmpl
	 294  	return tmpl
	 295  }
	 296  
	 297  // New allocates a new HTML template associated with the given one
	 298  // and with the same delimiters. The association, which is transitive,
	 299  // allows one template to invoke another with a {{template}} action.
	 300  //
	 301  // If a template with the given name already exists, the new HTML template
	 302  // will replace it. The existing template will be reset and disassociated with
	 303  // t.
	 304  func (t *Template) New(name string) *Template {
	 305  	t.nameSpace.mu.Lock()
	 306  	defer t.nameSpace.mu.Unlock()
	 307  	return t.new(name)
	 308  }
	 309  
	 310  // new is the implementation of New, without the lock.
	 311  func (t *Template) new(name string) *Template {
	 312  	tmpl := &Template{
	 313  		nil,
	 314  		t.text.New(name),
	 315  		nil,
	 316  		t.nameSpace,
	 317  	}
	 318  	if existing, ok := tmpl.set[name]; ok {
	 319  		emptyTmpl := New(existing.Name())
	 320  		*existing = *emptyTmpl
	 321  	}
	 322  	tmpl.set[name] = tmpl
	 323  	return tmpl
	 324  }
	 325  
	 326  // Name returns the name of the template.
	 327  func (t *Template) Name() string {
	 328  	return t.text.Name()
	 329  }
	 330  
	 331  // FuncMap is the type of the map defining the mapping from names to
	 332  // functions. Each function must have either a single return value, or two
	 333  // return values of which the second has type error. In that case, if the
	 334  // second (error) argument evaluates to non-nil during execution, execution
	 335  // terminates and Execute returns that error. FuncMap has the same base type
	 336  // as FuncMap in "text/template", copied here so clients need not import
	 337  // "text/template".
	 338  type FuncMap map[string]interface{}
	 339  
	 340  // Funcs adds the elements of the argument map to the template's function map.
	 341  // It must be called before the template is parsed.
	 342  // It panics if a value in the map is not a function with appropriate return
	 343  // type. However, it is legal to overwrite elements of the map. The return
	 344  // value is the template, so calls can be chained.
	 345  func (t *Template) Funcs(funcMap FuncMap) *Template {
	 346  	t.text.Funcs(template.FuncMap(funcMap))
	 347  	return t
	 348  }
	 349  
	 350  // Delims sets the action delimiters to the specified strings, to be used in
	 351  // subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
	 352  // definitions will inherit the settings. An empty delimiter stands for the
	 353  // corresponding default: {{ or }}.
	 354  // The return value is the template, so calls can be chained.
	 355  func (t *Template) Delims(left, right string) *Template {
	 356  	t.text.Delims(left, right)
	 357  	return t
	 358  }
	 359  
	 360  // Lookup returns the template with the given name that is associated with t,
	 361  // or nil if there is no such template.
	 362  func (t *Template) Lookup(name string) *Template {
	 363  	t.nameSpace.mu.Lock()
	 364  	defer t.nameSpace.mu.Unlock()
	 365  	return t.set[name]
	 366  }
	 367  
	 368  // Must is a helper that wraps a call to a function returning (*Template, error)
	 369  // and panics if the error is non-nil. It is intended for use in variable initializations
	 370  // such as
	 371  //	var t = template.Must(template.New("name").Parse("html"))
	 372  func Must(t *Template, err error) *Template {
	 373  	if err != nil {
	 374  		panic(err)
	 375  	}
	 376  	return t
	 377  }
	 378  
	 379  // ParseFiles creates a new Template and parses the template definitions from
	 380  // the named files. The returned template's name will have the (base) name and
	 381  // (parsed) contents of the first file. There must be at least one file.
	 382  // If an error occurs, parsing stops and the returned *Template is nil.
	 383  //
	 384  // When parsing multiple files with the same name in different directories,
	 385  // the last one mentioned will be the one that results.
	 386  // For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
	 387  // named "foo", while "a/foo" is unavailable.
	 388  func ParseFiles(filenames ...string) (*Template, error) {
	 389  	return parseFiles(nil, readFileOS, filenames...)
	 390  }
	 391  
	 392  // ParseFiles parses the named files and associates the resulting templates with
	 393  // t. If an error occurs, parsing stops and the returned template is nil;
	 394  // otherwise it is t. There must be at least one file.
	 395  //
	 396  // When parsing multiple files with the same name in different directories,
	 397  // the last one mentioned will be the one that results.
	 398  //
	 399  // ParseFiles returns an error if t or any associated template has already been executed.
	 400  func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
	 401  	return parseFiles(t, readFileOS, filenames...)
	 402  }
	 403  
	 404  // parseFiles is the helper for the method and function. If the argument
	 405  // template is nil, it is created from the first file.
	 406  func parseFiles(t *Template, readFile func(string) (string, []byte, error), filenames ...string) (*Template, error) {
	 407  	if err := t.checkCanParse(); err != nil {
	 408  		return nil, err
	 409  	}
	 410  
	 411  	if len(filenames) == 0 {
	 412  		// Not really a problem, but be consistent.
	 413  		return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
	 414  	}
	 415  	for _, filename := range filenames {
	 416  		name, b, err := readFile(filename)
	 417  		if err != nil {
	 418  			return nil, err
	 419  		}
	 420  		s := string(b)
	 421  		// First template becomes return value if not already defined,
	 422  		// and we use that one for subsequent New calls to associate
	 423  		// all the templates together. Also, if this file has the same name
	 424  		// as t, this file becomes the contents of t, so
	 425  		//	t, err := New(name).Funcs(xxx).ParseFiles(name)
	 426  		// works. Otherwise we create a new template associated with t.
	 427  		var tmpl *Template
	 428  		if t == nil {
	 429  			t = New(name)
	 430  		}
	 431  		if name == t.Name() {
	 432  			tmpl = t
	 433  		} else {
	 434  			tmpl = t.New(name)
	 435  		}
	 436  		_, err = tmpl.Parse(s)
	 437  		if err != nil {
	 438  			return nil, err
	 439  		}
	 440  	}
	 441  	return t, nil
	 442  }
	 443  
	 444  // ParseGlob creates a new Template and parses the template definitions from
	 445  // the files identified by the pattern. The files are matched according to the
	 446  // semantics of filepath.Match, and the pattern must match at least one file.
	 447  // The returned template will have the (base) name and (parsed) contents of the
	 448  // first file matched by the pattern. ParseGlob is equivalent to calling
	 449  // ParseFiles with the list of files matched by the pattern.
	 450  //
	 451  // When parsing multiple files with the same name in different directories,
	 452  // the last one mentioned will be the one that results.
	 453  func ParseGlob(pattern string) (*Template, error) {
	 454  	return parseGlob(nil, pattern)
	 455  }
	 456  
	 457  // ParseGlob parses the template definitions in the files identified by the
	 458  // pattern and associates the resulting templates with t. The files are matched
	 459  // according to the semantics of filepath.Match, and the pattern must match at
	 460  // least one file. ParseGlob is equivalent to calling t.ParseFiles with the
	 461  // list of files matched by the pattern.
	 462  //
	 463  // When parsing multiple files with the same name in different directories,
	 464  // the last one mentioned will be the one that results.
	 465  //
	 466  // ParseGlob returns an error if t or any associated template has already been executed.
	 467  func (t *Template) ParseGlob(pattern string) (*Template, error) {
	 468  	return parseGlob(t, pattern)
	 469  }
	 470  
	 471  // parseGlob is the implementation of the function and method ParseGlob.
	 472  func parseGlob(t *Template, pattern string) (*Template, error) {
	 473  	if err := t.checkCanParse(); err != nil {
	 474  		return nil, err
	 475  	}
	 476  	filenames, err := filepath.Glob(pattern)
	 477  	if err != nil {
	 478  		return nil, err
	 479  	}
	 480  	if len(filenames) == 0 {
	 481  		return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern)
	 482  	}
	 483  	return parseFiles(t, readFileOS, filenames...)
	 484  }
	 485  
	 486  // IsTrue reports whether the value is 'true', in the sense of not the zero of its type,
	 487  // and whether the value has a meaningful truth value. This is the definition of
	 488  // truth used by if and other such actions.
	 489  func IsTrue(val interface{}) (truth, ok bool) {
	 490  	return template.IsTrue(val)
	 491  }
	 492  
	 493  // ParseFS is like ParseFiles or ParseGlob but reads from the file system fs
	 494  // instead of the host operating system's file system.
	 495  // It accepts a list of glob patterns.
	 496  // (Note that most file names serve as glob patterns matching only themselves.)
	 497  func ParseFS(fs fs.FS, patterns ...string) (*Template, error) {
	 498  	return parseFS(nil, fs, patterns)
	 499  }
	 500  
	 501  // ParseFS is like ParseFiles or ParseGlob but reads from the file system fs
	 502  // instead of the host operating system's file system.
	 503  // It accepts a list of glob patterns.
	 504  // (Note that most file names serve as glob patterns matching only themselves.)
	 505  func (t *Template) ParseFS(fs fs.FS, patterns ...string) (*Template, error) {
	 506  	return parseFS(t, fs, patterns)
	 507  }
	 508  
	 509  func parseFS(t *Template, fsys fs.FS, patterns []string) (*Template, error) {
	 510  	var filenames []string
	 511  	for _, pattern := range patterns {
	 512  		list, err := fs.Glob(fsys, pattern)
	 513  		if err != nil {
	 514  			return nil, err
	 515  		}
	 516  		if len(list) == 0 {
	 517  			return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
	 518  		}
	 519  		filenames = append(filenames, list...)
	 520  	}
	 521  	return parseFiles(t, readFileFS(fsys), filenames...)
	 522  }
	 523  
	 524  func readFileOS(file string) (name string, b []byte, err error) {
	 525  	name = filepath.Base(file)
	 526  	b, err = os.ReadFile(file)
	 527  	return
	 528  }
	 529  
	 530  func readFileFS(fsys fs.FS) func(string) (string, []byte, error) {
	 531  	return func(file string) (name string, b []byte, err error) {
	 532  		name = path.Base(file)
	 533  		b, err = fs.ReadFile(fsys, file)
	 534  		return
	 535  	}
	 536  }
	 537  

View as plain text