summaryrefslogtreecommitdiff
path: root/lib/Template
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Template')
-rw-r--r--lib/Template/Base.pm290
-rw-r--r--lib/Template/Config.pm457
-rw-r--r--lib/Template/Constants.pm277
-rw-r--r--lib/Template/Context.pm1549
-rw-r--r--lib/Template/Directive.pm1004
-rw-r--r--lib/Template/Document.pm482
-rw-r--r--lib/Template/Exception.pm244
-rw-r--r--lib/Template/Filters.pm1438
-rw-r--r--lib/Template/Grammar.pm6174
-rw-r--r--lib/Template/Iterator.pm446
-rw-r--r--lib/Template/Namespace/Constants.pm195
-rw-r--r--lib/Template/Parser.pm1434
-rw-r--r--lib/Template/Plugin.pm399
-rw-r--r--lib/Template/Plugin/Date.pm361
-rw-r--r--lib/Template/Plugins.pm1031
-rw-r--r--lib/Template/Provider.pm1433
-rw-r--r--lib/Template/Service.pm765
-rw-r--r--lib/Template/Stash.pm1000
-rw-r--r--lib/Template/Stash/Context.pm781
-rw-r--r--lib/Template/Stash/XS.pm176
-rw-r--r--lib/Template/Test.pm701
-rw-r--r--lib/Template/View.pm754
22 files changed, 21391 insertions, 0 deletions
diff --git a/lib/Template/Base.pm b/lib/Template/Base.pm
new file mode 100644
index 0000000..b66d9c8
--- /dev/null
+++ b/lib/Template/Base.pm
@@ -0,0 +1,290 @@
+#============================================================= -*-perl-*-
+#
+# Template::Base
+#
+# DESCRIPTION
+# Base class module implementing common functionality for various other
+# Template Toolkit modules.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------
+#
+# $Id: Base.pm,v 2.62 2003/04/24 09:14:38 abw Exp $
+#
+#========================================================================
+
+package Template::Base;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new(\%params)
+#
+# General purpose constructor method which expects a hash reference of
+# configuration parameters, or a list of name => value pairs which are
+# folded into a hash. Blesses a hash into an object and calls its
+# _init() method, passing the parameter hash reference. Returns a new
+# object derived from Template::Base, or undef on error.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my ($argnames, @args, $arg, $cfg);
+# $class->error(''); # always clear package $ERROR var?
+
+ { no strict qw( refs );
+ $argnames = \@{"$class\::BASEARGS"} || [ ];
+ }
+
+ # shift off all mandatory args, returning error if undefined or null
+ foreach $arg (@$argnames) {
+ return $class->error("no $arg specified")
+ unless ($cfg = shift);
+ push(@args, $cfg);
+ }
+
+ # fold all remaining args into a hash, or use provided hash ref
+# local $" = ', ';
+# print STDERR "args: [@_]\n";
+ $cfg = defined $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift : { @_ };
+
+ my $self = bless {
+ map { ($_ => shift @args) } @$argnames,
+ _ERROR => '',
+ DEBUG => 0,
+ }, $class;
+
+ return $self->_init($cfg) ? $self : $class->error($self->error);
+}
+
+
+#------------------------------------------------------------------------
+# error()
+# error($msg, ...)
+#
+# May be called as a class or object method to set or retrieve the
+# package variable $ERROR (class method) or internal member
+# $self->{ _ERROR } (object method). The presence of parameters indicates
+# that the error value should be set. Undef is then returned. In the
+# abscence of parameters, the current error value is returned.
+#------------------------------------------------------------------------
+
+sub error {
+ my $self = shift;
+ my $errvar;
+
+ {
+ no strict qw( refs );
+ $errvar = ref $self ? \$self->{ _ERROR } : \${"$self\::ERROR"};
+ }
+ if (@_) {
+ $$errvar = ref($_[0]) ? shift : join('', @_);
+ return undef;
+ }
+ else {
+ return $$errvar;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# _init()
+#
+# Initialisation method called by the new() constructor and passing a
+# reference to a hash array containing any configuration items specified
+# as constructor arguments. Should return $self on success or undef on
+# error, via a call to the error() method to set the error message.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $config) = @_;
+ return $self;
+}
+
+
+sub DEBUG {
+ my $self = shift;
+ print STDERR "DEBUG: ", @_;
+}
+
+sub debug {
+ my $self = shift;
+ my $msg = join('', @_);
+ my ($pkg, $file, $line) = caller();
+
+ unless ($msg =~ /\n$/) {
+ $msg .= ($self->{ DEBUG } & Template::Constants::DEBUG_CALLER)
+ ? " at $file line $line\n"
+ : "\n";
+ }
+
+ print STDERR "[$pkg] $msg";
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Base - Base class module implementing common functionality
+
+=head1 SYNOPSIS
+
+ package My::Module;
+ use base qw( Template::Base );
+
+ sub _init {
+ my ($self, $config) = @_;
+ $self->{ doodah } = $config->{ doodah }
+ || return $self->error("No 'doodah' specified");
+ return $self;
+ }
+
+ package main;
+
+ my $object = My::Module->new({ doodah => 'foobar' })
+ || die My::Module->error();
+
+=head1 DESCRIPTION
+
+Base class module which implements a constructor and error reporting
+functionality for various Template Toolkit modules.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%config)
+
+Constructor method which accepts a reference to a hash array or a list
+of C<name =E<gt> value> parameters which are folded into a hash. The
+_init() method is then called, passing the configuration hash and should
+return true/false to indicate success or failure. A new object reference
+is returned, or undef on error. Any error message raised can be examined
+via the error() class method or directly via the package variable ERROR
+in the derived class.
+
+ my $module = My::Module->new({ ... })
+ || die My::Module->error(), "\n";
+
+ my $module = My::Module->new({ ... })
+ || die "constructor error: $My::Module::ERROR\n";
+
+=head2 error($msg, ...)
+
+May be called as an object method to get/set the internal _ERROR member
+or as a class method to get/set the $ERROR variable in the derived class's
+package.
+
+ my $module = My::Module->new({ ... })
+ || die My::Module->error(), "\n";
+
+ $module->do_something()
+ || die $module->error(), "\n";
+
+When called with parameters (multiple params are concatenated), this
+method will set the relevant variable and return undef. This is most
+often used within object methods to report errors to the caller.
+
+ package My::Module;
+
+ sub foobar {
+ my $self = shift;
+
+ # some other code...
+
+ return $self->error('some kind of error...')
+ if $some_condition;
+ }
+
+=head2 debug($msg, ...)
+
+Generates a debugging message by concatenating all arguments
+passed into a string and printing it to STDERR. A prefix is
+added to indicate the module of the caller.
+
+ package My::Module;
+
+ sub foobar {
+ my $self = shift;
+
+ $self->debug('called foobar()');
+
+ # some other code...
+ }
+
+When the foobar() method is called, the following message
+is sent to STDERR:
+
+ [My::Module] called foobar()
+
+Objects can set an internal DEBUG value which the debug()
+method will examine. If this value sets the relevant bits
+to indicate DEBUG_CALLER then the file and line number of
+the caller will be appened to the message.
+
+ use Template::Constants qw( :debug );
+
+ my $module = My::Module->new({
+ DEBUG => DEBUG_SERVICE | DEBUG_CONTEXT | DEBUG_CALLER,
+ });
+
+ $module->foobar();
+
+This generates an error message such as:
+
+ [My::Module] called foobar() at My/Module.pm line 6
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.62, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>
diff --git a/lib/Template/Config.pm b/lib/Template/Config.pm
new file mode 100644
index 0000000..dbe3a53
--- /dev/null
+++ b/lib/Template/Config.pm
@@ -0,0 +1,457 @@
+#============================================================= -*-perl-*-
+#
+# Template::Config
+#
+# DESCRIPTION
+# Template Toolkit configuration module.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------
+#
+# $Id: Config.pm,v 2.62 2003/04/24 09:14:38 abw Exp $
+#
+#========================================================================
+
+package Template::Config;
+
+require 5.004;
+
+use strict;
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG $ERROR $INSTDIR
+ $PARSER $PROVIDER $PLUGINS $FILTERS $ITERATOR
+ $LATEX_PATH $PDFLATEX_PATH $DVIPS_PATH
+ $STASH $SERVICE $CONTEXT $CONSTANTS @PRELOAD );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+$ERROR = '';
+$CONTEXT = 'Template::Context';
+$FILTERS = 'Template::Filters';
+$ITERATOR = 'Template::Iterator';
+$PARSER = 'Template::Parser';
+$PLUGINS = 'Template::Plugins';
+$PROVIDER = 'Template::Provider';
+$SERVICE = 'Template::Service';
+$STASH = 'Template::Stash';
+$CONSTANTS = 'Template::Namespace::Constants';
+
+@PRELOAD = ( $CONTEXT, $FILTERS, $ITERATOR, $PARSER,
+ $PLUGINS, $PROVIDER, $SERVICE, $STASH );
+
+# the following is set at installation time by the Makefile.PL
+$INSTDIR = '';
+
+# LaTeX executable paths set at installation time by the Makefile.PL
+# Empty strings cause the latex(pdf|dvi|ps) filters to throw an error.
+$LATEX_PATH = '';
+$PDFLATEX_PATH = '';
+$DVIPS_PATH = '';
+
+#========================================================================
+# --- CLASS METHODS ---
+#========================================================================
+
+#------------------------------------------------------------------------
+# preload($module, $module, ...)
+#
+# Preloads all the standard TT modules that are likely to be used, along
+# with any other passed as arguments.
+#------------------------------------------------------------------------
+
+sub preload {
+ my $class = shift;
+
+ foreach my $module (@PRELOAD, @_) {
+ $class->load($module) || return;
+ };
+ return 1;
+}
+
+
+#------------------------------------------------------------------------
+# load($module)
+#
+# Load a module via require(). Any occurences of '::' in the module name
+# are be converted to '/' and '.pm' is appended. Returns 1 on success
+# or undef on error. Use $class->error() to examine the error string.
+#------------------------------------------------------------------------
+
+sub load {
+ my ($class, $module) = @_;
+ $module =~ s[::][/]g;
+ $module .= '.pm';
+# print STDERR "loading $module\n"
+# if $DEBUG;
+ eval {
+ require $module;
+ };
+ return $@ ? $class->error("failed to load $module: $@") : 1;
+}
+
+
+#------------------------------------------------------------------------
+# parser(\%params)
+#
+# Instantiate a new parser object of the class whose name is denoted by
+# the package variable $PARSER (default: Template::Parser). Returns
+# a reference to a newly instantiated parser object or undef on error.
+# The class error() method can be called without arguments to examine
+# the error message generated by this failure.
+#------------------------------------------------------------------------
+
+sub parser {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($PARSER);
+ return $PARSER->new($params)
+ || $class->error("failed to create parser: ", $PARSER->error);
+}
+
+
+#------------------------------------------------------------------------
+# provider(\%params)
+#
+# Instantiate a new template provider object (default: Template::Provider).
+# Returns an object reference or undef on error, as above.
+#------------------------------------------------------------------------
+
+sub provider {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($PROVIDER);
+ return $PROVIDER->new($params)
+ || $class->error("failed to create template provider: ",
+ $PROVIDER->error);
+}
+
+
+#------------------------------------------------------------------------
+# plugins(\%params)
+#
+# Instantiate a new plugins provider object (default: Template::Plugins).
+# Returns an object reference or undef on error, as above.
+#------------------------------------------------------------------------
+
+sub plugins {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($PLUGINS);
+ return $PLUGINS->new($params)
+ || $class->error("failed to create plugin provider: ",
+ $PLUGINS->error);
+}
+
+
+#------------------------------------------------------------------------
+# filters(\%params)
+#
+# Instantiate a new filters provider object (default: Template::Filters).
+# Returns an object reference or undef on error, as above.
+#------------------------------------------------------------------------
+
+sub filters {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($FILTERS);
+ return $FILTERS->new($params)
+ || $class->error("failed to create filter provider: ",
+ $FILTERS->error);
+}
+
+
+#------------------------------------------------------------------------
+# iterator(\@list)
+#
+# Instantiate a new Template::Iterator object (default: Template::Iterator).
+# Returns an object reference or undef on error, as above.
+#------------------------------------------------------------------------
+
+sub iterator {
+ my $class = shift;
+ my $list = shift;
+
+ return undef unless $class->load($ITERATOR);
+ return $ITERATOR->new($list, @_)
+ || $class->error("failed to create iterator: ", $ITERATOR->error);
+}
+
+
+#------------------------------------------------------------------------
+# stash(\%vars)
+#
+# Instantiate a new template variable stash object (default:
+# Template::Stash). Returns object or undef, as above.
+#------------------------------------------------------------------------
+
+sub stash {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($STASH);
+ return $STASH->new($params)
+ || $class->error("failed to create stash: ", $STASH->error);
+}
+
+
+#------------------------------------------------------------------------
+# context(\%params)
+#
+# Instantiate a new template context object (default: Template::Context).
+# Returns object or undef, as above.
+#------------------------------------------------------------------------
+
+sub context {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($CONTEXT);
+ return $CONTEXT->new($params)
+ || $class->error("failed to create context: ", $CONTEXT->error);
+}
+
+
+#------------------------------------------------------------------------
+# service(\%params)
+#
+# Instantiate a new template context object (default: Template::Service).
+# Returns object or undef, as above.
+#------------------------------------------------------------------------
+
+sub service {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($SERVICE);
+ return $SERVICE->new($params)
+ || $class->error("failed to create context: ", $SERVICE->error);
+}
+
+
+#------------------------------------------------------------------------
+# constants(\%params)
+#
+# Instantiate a new namespace handler for compile time constant folding
+# (default: Template::Namespace::Constants).
+# Returns object or undef, as above.
+#------------------------------------------------------------------------
+
+sub constants {
+ my $class = shift;
+ my $params = defined($_[0]) && UNIVERSAL::isa($_[0], 'HASH')
+ ? shift : { @_ };
+
+ return undef unless $class->load($CONSTANTS);
+ return $CONSTANTS->new($params)
+ || $class->error("failed to create constants namespace: ",
+ $CONSTANTS->error);
+}
+
+
+#------------------------------------------------------------------------
+# instdir($dir)
+#
+# Returns the root installation directory appended with any local
+# component directory passed as an argument.
+#------------------------------------------------------------------------
+
+sub instdir {
+ my ($class, $dir) = @_;
+ my $inst = $INSTDIR
+ || return $class->error("no installation directory");
+ $inst =~ s[/$][]g;
+ $inst .= "/$dir" if $dir;
+ return $inst;
+}
+
+#------------------------------------------------------------------------
+# latexpaths()
+#
+# Returns a reference to a three element array:
+# [latex_path, pdf2latex_path, dvips_path]
+# These values are determined by Makefile.PL at installation time
+# and are used by the latex(pdf|dvi|ps) filters.
+#------------------------------------------------------------------------
+
+sub latexpaths {
+ return [$LATEX_PATH, $PDFLATEX_PATH, $DVIPS_PATH];
+}
+
+#========================================================================
+# This should probably be moved somewhere else in the long term, but for
+# now it ensures that Template::TieString is available even if the
+# Template::Directive module hasn't been loaded, as is the case when
+# using compiled templates and Template::Parser hasn't yet been loaded
+# on demand.
+#========================================================================
+
+#------------------------------------------------------------------------
+# simple package for tying $output variable to STDOUT, used by perl()
+#------------------------------------------------------------------------
+
+package Template::TieString;
+
+sub TIEHANDLE {
+ my ($class, $textref) = @_;
+ bless $textref, $class;
+}
+sub PRINT {
+ my $self = shift;
+ $$self .= join('', @_);
+}
+
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Config - Factory module for instantiating other TT2 modules
+
+=head1 SYNOPSIS
+
+ use Template::Config;
+
+=head1 DESCRIPTION
+
+This module implements various methods for loading and instantiating
+other modules that comprise the Template Toolkit. It provides a consistent
+way to create toolkit components and allows custom modules to be used in
+place of the regular ones.
+
+Package variables such as $STASH, $SERVICE, $CONTEXT, etc., contain
+the default module/package name for each component (Template::Stash,
+Template::Service and Template::Context, respectively) and are used by
+the various factory methods (stash(), service() and context()) to load
+the appropriate module. Changing these package variables will cause
+subsequent calls to the relevant factory method to load and instantiate
+an object from the new class.
+
+=head1 PUBLIC METHODS
+
+=head2 load($module)
+
+Load a module via require(). Any occurences of '::' in the module name
+are be converted to '/' and '.pm' is appended. Returns 1 on success
+or undef on error. Use $class-E<gt>error() to examine the error string.
+
+=head2 preload()
+
+This method preloads all the other Template::* modules that are likely
+to be used. It is called by the Template module when running under
+mod_perl ($ENV{MOD_PERL} is set).
+
+=head2 parser(\%config)
+
+Instantiate a new parser object of the class whose name is denoted by
+the package variable $PARSER (default: Template::Parser). Returns
+a reference to a newly instantiated parser object or undef on error.
+
+=head2 provider(\%config)
+
+Instantiate a new template provider object (default: Template::Provider).
+Returns an object reference or undef on error, as above.
+
+=head2 plugins(\%config)
+
+Instantiate a new plugins provider object (default: Template::Plugins).
+Returns an object reference or undef on error, as above.
+
+=head2 filters(\%config)
+
+Instantiate a new filter provider object (default: Template::Filters).
+Returns an object reference or undef on error, as above.
+
+=head2 stash(\%vars)
+
+Instantiate a new stash object (Template::Stash or Template::Stash::XS
+depending on the default set at installation time) using the contents
+of the optional hash array passed by parameter as initial variable
+definitions. Returns an object reference or undef on error, as above.
+
+=head2 context(\%config)
+
+Instantiate a new template context object (default: Template::Context).
+Returns an object reference or undef on error, as above.
+
+=head2 service(\%config)
+
+Instantiate a new template service object (default: Template::Service).
+Returns an object reference or undef on error, as above.
+
+=head2 instdir($dir)
+
+Returns the root directory of the Template Toolkit installation under
+which optional components are installed. Any relative directory specified
+as an argument will be appended to the returned directory.
+
+ # e.g. returns '/usr/local/tt2'
+ my $ttroot = Template::Config->instdir()
+ || die "$Template::Config::ERROR\n";
+
+ # e.g. returns '/usr/local/tt2/templates'
+ my $template = Template::Config->instdir('templates')
+ || die "$Template::Config::ERROR\n";
+
+Returns undef and sets $Template::Config::ERROR appropriately if the
+optional components of the Template Toolkit have not been installed.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.62, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>
diff --git a/lib/Template/Constants.pm b/lib/Template/Constants.pm
new file mode 100644
index 0000000..60af6bb
--- /dev/null
+++ b/lib/Template/Constants.pm
@@ -0,0 +1,277 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Constants.pm
+#
+# DESCRIPTION
+# Definition of constants for the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Constants.pm,v 2.62 2003/04/24 09:14:38 abw Exp $
+#
+#============================================================================
+
+package Template::Constants;
+
+require 5.004;
+require Exporter;
+
+use strict;
+use vars qw( $VERSION @ISA @EXPORT_OK %EXPORT_TAGS );
+use vars qw( $DEBUG_OPTIONS @STATUS @ERROR @CHOMP @DEBUG);
+
+@ISA = qw( Exporter );
+$VERSION = sprintf("%d.%02d", q$Revision: 2.62 $ =~ /(\d+)\.(\d+)/);
+
+
+#========================================================================
+# ----- EXPORTER -----
+#========================================================================
+
+# STATUS constants returned by directives
+use constant STATUS_OK => 0; # ok
+use constant STATUS_RETURN => 1; # ok, block ended by RETURN
+use constant STATUS_STOP => 2; # ok, stoppped by STOP
+use constant STATUS_DONE => 3; # ok, iterator done
+use constant STATUS_DECLINED => 4; # ok, declined to service request
+use constant STATUS_ERROR => 255; # error condition
+
+# ERROR constants for indicating exception types
+use constant ERROR_RETURN => 'return'; # return a status code
+use constant ERROR_FILE => 'file'; # file error: I/O, parse, recursion
+use constant ERROR_VIEW => 'view'; # view error
+use constant ERROR_UNDEF => 'undef'; # undefined variable value used
+use constant ERROR_PERL => 'perl'; # error in [% PERL %] block
+use constant ERROR_FILTER => 'filter'; # filter error
+use constant ERROR_PLUGIN => 'plugin'; # plugin error
+
+# CHOMP constants for PRE_CHOMP and POST_CHOMP
+use constant CHOMP_NONE => 0; # do not remove whitespace
+use constant CHOMP_ALL => 1; # remove whitespace
+use constant CHOMP_COLLAPSE => 2; # collapse whitespace to a single space
+
+# DEBUG constants to enable various debugging options
+use constant DEBUG_OFF => 0; # do nothing
+use constant DEBUG_ON => 1; # basic debugging flag
+use constant DEBUG_UNDEF => 2; # throw undef on undefined variables
+use constant DEBUG_VARS => 4; # general variable debugging
+use constant DEBUG_DIRS => 8; # directive debugging
+use constant DEBUG_STASH => 16; # general stash debugging
+use constant DEBUG_CONTEXT => 32; # context debugging
+use constant DEBUG_PARSER => 64; # parser debugging
+use constant DEBUG_PROVIDER => 128; # provider debugging
+use constant DEBUG_PLUGINS => 256; # plugins debugging
+use constant DEBUG_FILTERS => 512; # filters debugging
+use constant DEBUG_SERVICE => 1024; # context debugging
+use constant DEBUG_ALL => 2047; # everything
+
+# extra debugging flags
+use constant DEBUG_CALLER => 4096; # add caller file/line
+use constant DEBUG_FLAGS => 4096; # bitmask to extraxt flags
+
+$DEBUG_OPTIONS = {
+ &DEBUG_OFF => off => off => &DEBUG_OFF,
+ &DEBUG_ON => on => on => &DEBUG_ON,
+ &DEBUG_UNDEF => undef => undef => &DEBUG_UNDEF,
+ &DEBUG_VARS => vars => vars => &DEBUG_VARS,
+ &DEBUG_DIRS => dirs => dirs => &DEBUG_DIRS,
+ &DEBUG_STASH => stash => stash => &DEBUG_STASH,
+ &DEBUG_CONTEXT => context => context => &DEBUG_CONTEXT,
+ &DEBUG_PARSER => parser => parser => &DEBUG_PARSER,
+ &DEBUG_PROVIDER => provider => provider => &DEBUG_PROVIDER,
+ &DEBUG_PLUGINS => plugins => plugins => &DEBUG_PLUGINS,
+ &DEBUG_FILTERS => filters => filters => &DEBUG_FILTERS,
+ &DEBUG_SERVICE => service => service => &DEBUG_SERVICE,
+ &DEBUG_ALL => all => all => &DEBUG_ALL,
+ &DEBUG_CALLER => caller => caller => &DEBUG_CALLER,
+};
+
+@STATUS = qw( STATUS_OK STATUS_RETURN STATUS_STOP STATUS_DONE
+ STATUS_DECLINED STATUS_ERROR );
+@ERROR = qw( ERROR_FILE ERROR_VIEW ERROR_UNDEF ERROR_PERL
+ ERROR_RETURN ERROR_FILTER ERROR_PLUGIN );
+@CHOMP = qw( CHOMP_NONE CHOMP_ALL CHOMP_COLLAPSE );
+@DEBUG = qw( DEBUG_OFF DEBUG_ON DEBUG_UNDEF DEBUG_VARS
+ DEBUG_DIRS DEBUG_STASH DEBUG_CONTEXT DEBUG_PARSER
+ DEBUG_PROVIDER DEBUG_PLUGINS DEBUG_FILTERS DEBUG_SERVICE
+ DEBUG_ALL DEBUG_CALLER DEBUG_FLAGS );
+
+@EXPORT_OK = ( @STATUS, @ERROR, @CHOMP, @DEBUG );
+%EXPORT_TAGS = (
+ 'all' => [ @EXPORT_OK ],
+ 'status' => [ @STATUS ],
+ 'error' => [ @ERROR ],
+ 'chomp' => [ @CHOMP ],
+ 'debug' => [ @DEBUG ],
+);
+
+
+sub debug_flags {
+ my ($self, $debug) = @_;
+ my (@flags, $flag, $value);
+ $debug = $self unless defined($debug) || ref($self);
+
+ if ($debug =~ /^\d+$/) {
+ foreach $flag (@DEBUG) {
+ next if $flag =~ /^DEBUG_(OFF|ALL|FLAGS)$/;
+
+ # don't trash the original
+ my $copy = $flag;
+ $flag =~ s/^DEBUG_//;
+ $flag = lc $flag;
+ return $self->error("no value for flag: $flag")
+ unless defined($value = $DEBUG_OPTIONS->{ $flag });
+ $flag = $value;
+
+ if ($debug & $flag) {
+ $value = $DEBUG_OPTIONS->{ $flag };
+ return $self->error("no value for flag: $flag") unless defined $value;
+ push(@flags, $value);
+ }
+ }
+ return wantarray ? @flags : join(', ', @flags);
+ }
+ else {
+ @flags = split(/\W+/, $debug);
+ $debug = 0;
+ foreach $flag (@flags) {
+ $value = $DEBUG_OPTIONS->{ $flag };
+ return $self->error("unknown debug flag: $flag") unless defined $value;
+ $debug |= $value;
+ }
+ return $debug;
+ }
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Constants - Defines constants for the Template Toolkit
+
+=head1 SYNOPSIS
+
+ use Template::Constants qw( :status :error :all );
+
+=head1 DESCRIPTION
+
+The Template::Constants modules defines, and optionally exports into the
+caller's namespace, a number of constants used by the Template package.
+
+Constants may be used by specifying the Template::Constants package
+explicitly:
+
+ use Template::Constants;
+
+ print Template::Constants::STATUS_DECLINED;
+
+Constants may be imported into the caller's namespace by naming them as
+options to the C<use Template::Constants> statement:
+
+ use Template::Constants qw( STATUS_DECLINED );
+
+ print STATUS_DECLINED;
+
+Alternatively, one of the following tagset identifiers may be specified
+to import sets of constants; :status, :error, :all.
+
+ use Template::Constants qw( :status );
+
+ print STATUS_DECLINED;
+
+See L<Exporter> for more information on exporting variables.
+
+=head1 EXPORTABLE TAG SETS
+
+The following tag sets and associated constants are defined:
+
+ :status
+ STATUS_OK # no problem, continue
+ STATUS_RETURN # ended current block then continue (ok)
+ STATUS_STOP # controlled stop (ok)
+ STATUS_DONE # iterator is all done (ok)
+ STATUS_DECLINED # provider declined to service request (ok)
+ STATUS_ERROR # general error condition (not ok)
+
+ :error
+ ERROR_RETURN # return a status code (e.g. 'stop')
+ ERROR_FILE # file error: I/O, parse, recursion
+ ERROR_UNDEF # undefined variable value used
+ ERROR_PERL # error in [% PERL %] block
+ ERROR_FILTER # filter error
+ ERROR_PLUGIN # plugin error
+
+ :chomp # for PRE_CHOMP and POST_CHOMP
+ CHOMP_NONE # do not remove whitespace
+ CHOMP_ALL # remove whitespace
+ CHOMP_COLLAPSE # collapse whitespace to a single space
+
+ :debug
+ DEBUG_OFF # do nothing
+ DEBUG_ON # basic debugging flag
+ DEBUG_UNDEF # throw undef on undefined variables
+ DEBUG_VARS # general variable debugging
+ DEBUG_DIRS # directive debugging
+ DEBUG_STASH # general stash debugging
+ DEBUG_CONTEXT # context debugging
+ DEBUG_PARSER # parser debugging
+ DEBUG_PROVIDER # provider debugging
+ DEBUG_PLUGINS # plugins debugging
+ DEBUG_FILTERS # filters debugging
+ DEBUG_SERVICE # context debugging
+ DEBUG_ALL # everything
+ DEBUG_CALLER # add caller file/line info
+ DEBUG_FLAGS # bitmap used internally
+
+ :all All the above constants.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.62, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Exporter|Exporter>
diff --git a/lib/Template/Context.pm b/lib/Template/Context.pm
new file mode 100644
index 0000000..6fb29ca
--- /dev/null
+++ b/lib/Template/Context.pm
@@ -0,0 +1,1549 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Context
+#
+# DESCRIPTION
+# Module defining a context in which a template document is processed.
+# This is the runtime processing interface through which templates
+# can access the functionality of the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Context.pm,v 2.81 2003/07/24 11:32:35 abw Exp $
+#
+#============================================================================
+
+package Template::Context;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $AUTOLOAD $DEBUG_FORMAT );
+use base qw( Template::Base );
+
+use Template::Base;
+use Template::Config;
+use Template::Constants;
+use Template::Exception;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.81 $ =~ /(\d+)\.(\d+)/);
+$DEBUG_FORMAT = "\n## \$file line \$line : [% \$text %] ##\n";
+
+
+#========================================================================
+# ----- PUBLIC METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# template($name)
+#
+# General purpose method to fetch a template and return it in compiled
+# form. In the usual case, the $name parameter will be a simple string
+# containing the name of a template (e.g. 'header'). It may also be
+# a reference to Template::Document object (or sub-class) or a Perl
+# sub-routine. These are considered to be compiled templates and are
+# returned intact. Finally, it may be a reference to any other kind
+# of valid input source accepted by Template::Provider (e.g. scalar
+# ref, glob, IO handle, etc).
+#
+# Templates may be cached at one of 3 different levels. The internal
+# BLOCKS member is a local cache which holds references to all
+# template blocks used or imported via PROCESS since the context's
+# reset() method was last called. This is checked first and if the
+# template is not found, the method then walks down the BLOCKSTACK
+# list. This contains references to the block definition tables in
+# any enclosing Template::Documents that we're visiting (e.g. we've
+# been called via an INCLUDE and we want to access a BLOCK defined in
+# the template that INCLUDE'd us). If nothing is defined, then we
+# iterate through the LOAD_TEMPLATES providers list as a 'chain of
+# responsibility' (see Design Patterns) asking each object to fetch()
+# the template if it can.
+#
+# Returns the compiled template. On error, undef is returned and
+# the internal ERROR value (read via error()) is set to contain an
+# error message of the form "$name: $error".
+#------------------------------------------------------------------------
+
+sub template {
+ my ($self, $name) = @_;
+ my ($prefix, $blocks, $defblocks, $provider, $template, $error);
+ my ($shortname, $blockname, $providers);
+
+ $self->debug("template($name)") if $self->{ DEBUG };
+
+ # references to Template::Document (or sub-class) objects objects, or
+ # CODE references are assumed to be pre-compiled templates and are
+ # returned intact
+ return $name
+ if UNIVERSAL::isa($name, 'Template::Document')
+ || ref($name) eq 'CODE';
+
+ $shortname = $name;
+
+ unless (ref $name) {
+
+ $self->debug("looking for block [$name]") if $self->{ DEBUG };
+
+ # we first look in the BLOCKS hash for a BLOCK that may have
+ # been imported from a template (via PROCESS)
+ return $template
+ if ($template = $self->{ BLOCKS }->{ $name });
+
+ # then we iterate through the BLKSTACK list to see if any of the
+ # Template::Documents we're visiting define this BLOCK
+ foreach $blocks (@{ $self->{ BLKSTACK } }) {
+ return $template
+ if $blocks && ($template = $blocks->{ $name });
+ }
+
+ # now it's time to ask the providers, so we look to see if any
+ # prefix is specified to indicate the desired provider set.
+ if ($^O eq 'MSWin32') {
+ # let C:/foo through
+ $prefix = $1 if $shortname =~ s/^(\w{2,})://o;
+ }
+ else {
+ $prefix = $1 if $shortname =~ s/^(\w+)://;
+ }
+
+ if (defined $prefix) {
+ $providers = $self->{ PREFIX_MAP }->{ $prefix }
+ || return $self->throw(Template::Constants::ERROR_FILE,
+ "no providers for template prefix '$prefix'");
+ }
+ }
+ $providers = $self->{ PREFIX_MAP }->{ default }
+ || $self->{ LOAD_TEMPLATES }
+ unless $providers;
+
+
+ # Finally we try the regular template providers which will
+ # handle references to files, text, etc., as well as templates
+ # reference by name. If
+
+ $blockname = '';
+ while ($shortname) {
+ $self->debug("asking providers for [$shortname] [$blockname]")
+ if $self->{ DEBUG };
+
+ foreach my $provider (@$providers) {
+ ($template, $error) = $provider->fetch($shortname, $prefix);
+ if ($error) {
+ if ($error == Template::Constants::STATUS_ERROR) {
+ # $template contains exception object
+ if (UNIVERSAL::isa($template, 'Template::Exception')
+ && $template->type() eq Template::Constants::ERROR_FILE) {
+ $self->throw($template);
+ }
+ else {
+ $self->throw( Template::Constants::ERROR_FILE, $template );
+ }
+ }
+ # DECLINE is ok, carry on
+ }
+ elsif (length $blockname) {
+ return $template
+ if $template = $template->blocks->{ $blockname };
+ }
+ else {
+ return $template;
+ }
+ }
+
+ last if ref $shortname || ! $self->{ EXPOSE_BLOCKS };
+ $shortname =~ s{/([^/]+)$}{} || last;
+ $blockname = length $blockname ? "$1/$blockname" : $1;
+ }
+
+ $self->throw(Template::Constants::ERROR_FILE, "$name: not found");
+}
+
+
+#------------------------------------------------------------------------
+# plugin($name, \@args)
+#
+# Calls on each of the LOAD_PLUGINS providers in turn to fetch() (i.e. load
+# and instantiate) a plugin of the specified name. Additional parameters
+# passed are propagated to the new() constructor for the plugin.
+# Returns a reference to a new plugin object or other reference. On
+# error, undef is returned and the appropriate error message is set for
+# subsequent retrieval via error().
+#------------------------------------------------------------------------
+
+sub plugin {
+ my ($self, $name, $args) = @_;
+ my ($provider, $plugin, $error);
+
+ $self->debug("plugin($name, ", defined $args ? @$args : '[ ]', ')')
+ if $self->{ DEBUG };
+
+ # request the named plugin from each of the LOAD_PLUGINS providers in turn
+ foreach my $provider (@{ $self->{ LOAD_PLUGINS } }) {
+ ($plugin, $error) = $provider->fetch($name, $args, $self);
+ return $plugin unless $error;
+ if ($error == Template::Constants::STATUS_ERROR) {
+ $self->throw($plugin) if ref $plugin;
+ $self->throw(Template::Constants::ERROR_PLUGIN, $plugin);
+ }
+ }
+
+ $self->throw(Template::Constants::ERROR_PLUGIN, "$name: plugin not found");
+}
+
+
+#------------------------------------------------------------------------
+# filter($name, \@args, $alias)
+#
+# Similar to plugin() above, but querying the LOAD_FILTERS providers to
+# return filter instances. An alias may be provided which is used to
+# save the returned filter in a local cache.
+#------------------------------------------------------------------------
+
+sub filter {
+ my ($self, $name, $args, $alias) = @_;
+ my ($provider, $filter, $error);
+
+ $self->debug("filter($name, ",
+ defined $args ? @$args : '[ ]',
+ defined $alias ? $alias : '<no alias>', ')')
+ if $self->{ DEBUG };
+
+ # use any cached version of the filter if no params provided
+ return $filter
+ if ! $args && ! ref $name
+ && ($filter = $self->{ FILTER_CACHE }->{ $name });
+
+ # request the named filter from each of the FILTERS providers in turn
+ foreach my $provider (@{ $self->{ LOAD_FILTERS } }) {
+ ($filter, $error) = $provider->fetch($name, $args, $self);
+ last unless $error;
+ if ($error == Template::Constants::STATUS_ERROR) {
+ $self->throw($filter) if ref $filter;
+ $self->throw(Template::Constants::ERROR_FILTER, $filter);
+ }
+ # return $self->error($filter)
+ # if $error == &Template::Constants::STATUS_ERROR;
+ }
+
+ return $self->error("$name: filter not found")
+ unless $filter;
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # commented out by abw on 19 Nov 2001 to fix problem with xmlstyle
+ # plugin which may re-define a filter by calling define_filter()
+ # multiple times. With the automatic aliasing/caching below, any
+ # new filter definition isn't seen. Don't think this will cause
+ # any problems as filters explicitly supplied with aliases will
+ # still work as expected.
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # alias defaults to name if undefined
+ # $alias = $name
+ # unless defined($alias) or ref($name) or $args;
+
+ # cache FILTER if alias is valid
+ $self->{ FILTER_CACHE }->{ $alias } = $filter
+ if $alias;
+
+ return $filter;
+}
+
+
+#------------------------------------------------------------------------
+# view(\%config)
+#
+# Create a new Template::View bound to this context.
+#------------------------------------------------------------------------
+
+sub view {
+ my $self = shift;
+ require Template::View;
+ return Template::View->new($self, @_)
+ || $self->throw(&Template::Constants::ERROR_VIEW,
+ $Template::View::ERROR);
+}
+
+
+#------------------------------------------------------------------------
+# process($template, \%params) [% PROCESS template var=val ... %]
+# process($template, \%params, $local) [% INCLUDE template var=val ... %]
+#
+# Processes the template named or referenced by the first parameter.
+# The optional second parameter may reference a hash array of variable
+# definitions. These are set before the template is processed by
+# calling update() on the stash. Note that, unless the third parameter
+# is true, the context is not localised and these, and any other
+# variables set in the template will retain their new values after this
+# method returns. The third parameter is in place so that this method
+# can handle INCLUDE calls: the stash will be localized.
+#
+# Returns the output of processing the template. Errors are thrown
+# as Template::Exception objects via die().
+#------------------------------------------------------------------------
+
+sub process {
+ my ($self, $template, $params, $localize) = @_;
+ my ($trim, $blocks) = @$self{ qw( TRIM BLOCKS ) };
+ my (@compiled, $name, $compiled);
+ my ($stash, $tblocks, $error, $tmpout);
+ my $output = '';
+
+ $template = [ $template ] unless ref $template eq 'ARRAY';
+
+ $self->debug("process([ ", join(', '), @$template, ' ], ',
+ defined $params ? $params : '<no params>', ', ',
+ $localize ? '<localized>' : '<unlocalized>', ')')
+ if $self->{ DEBUG };
+
+ # fetch compiled template for each name specified
+ foreach $name (@$template) {
+ push(@compiled, $self->template($name));
+ }
+
+ if ($localize) {
+ # localise the variable stash with any parameters passed
+ $stash = $self->{ STASH } = $self->{ STASH }->clone($params);
+ } else {
+ # update stash with any new parameters passed
+ $self->{ STASH }->update($params);
+ $stash = $self->{ STASH };
+ }
+
+ eval {
+ foreach $name (@$template) {
+ $compiled = shift @compiled;
+ my $element = ref $compiled eq 'CODE'
+ ? { (name => (ref $name ? '' : $name), modtime => time()) }
+ : $compiled;
+ $stash->set('component', $element);
+
+ unless ($localize) {
+ # merge any local blocks defined in the Template::Document
+ # into our local BLOCKS cache
+ @$blocks{ keys %$tblocks } = values %$tblocks
+ if UNIVERSAL::isa($compiled, 'Template::Document')
+ && ($tblocks = $compiled->blocks());
+ }
+
+ if (ref $compiled eq 'CODE') {
+ $tmpout = &$compiled($self);
+ }
+ elsif (ref $compiled) {
+ $tmpout = $compiled->process($self);
+ }
+ else {
+ $self->throw('file',
+ "invalid template reference: $compiled");
+ }
+
+ if ($trim) {
+ for ($tmpout) {
+ s/^\s+//;
+ s/\s+$//;
+ }
+ }
+ $output .= $tmpout;
+ }
+ };
+ $error = $@;
+
+ if ($localize) {
+ # ensure stash is delocalised before dying
+ $self->{ STASH } = $self->{ STASH }->declone();
+ }
+
+ $self->throw(ref $error
+ ? $error : (Template::Constants::ERROR_FILE, $error))
+ if $error;
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# include($template, \%params) [% INCLUDE template var = val, ... %]
+#
+# Similar to process() above but processing the template in a local
+# context. Any variables passed by reference to a hash as the second
+# parameter will be set before the template is processed and then
+# revert to their original values before the method returns. Similarly,
+# any changes made to non-global variables within the template will
+# persist only until the template is processed.
+#
+# Returns the output of processing the template. Errors are thrown
+# as Template::Exception objects via die().
+#------------------------------------------------------------------------
+
+sub include {
+ my ($self, $template, $params) = @_;
+ return $self->process($template, $params, 'localize me!');
+}
+
+#------------------------------------------------------------------------
+# insert($file)
+#
+# Insert the contents of a file without parsing.
+#------------------------------------------------------------------------
+
+sub insert {
+ my ($self, $file) = @_;
+ my ($prefix, $providers, $text, $error);
+ my $output = '';
+
+ my $files = ref $file eq 'ARRAY' ? $file : [ $file ];
+
+ $self->debug("insert([ ", join(', '), @$files, " ])")
+ if $self->{ DEBUG };
+
+
+ FILE: foreach $file (@$files) {
+ my $name = $file;
+
+ if ($^O eq 'MSWin32') {
+ # let C:/foo through
+ $prefix = $1 if $name =~ s/^(\w{2,})://o;
+ }
+ else {
+ $prefix = $1 if $name =~ s/^(\w+)://;
+ }
+
+ if (defined $prefix) {
+ $providers = $self->{ PREFIX_MAP }->{ $prefix }
+ || return $self->throw(Template::Constants::ERROR_FILE,
+ "no providers for file prefix '$prefix'");
+ }
+ else {
+ $providers = $self->{ PREFIX_MAP }->{ default }
+ || $self->{ LOAD_TEMPLATES };
+ }
+
+ foreach my $provider (@$providers) {
+ ($text, $error) = $provider->load($name, $prefix);
+ next FILE unless $error;
+ if ($error == Template::Constants::STATUS_ERROR) {
+ $self->throw($text) if ref $text;
+ $self->throw(Template::Constants::ERROR_FILE, $text);
+ }
+ }
+ $self->throw(Template::Constants::ERROR_FILE, "$file: not found");
+ }
+ continue {
+ $output .= $text;
+ }
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# throw($type, $info, \$output) [% THROW errtype "Error info" %]
+#
+# Throws a Template::Exception object by calling die(). This method
+# may be passed a reference to an existing Template::Exception object;
+# a single value containing an error message which is used to
+# instantiate a Template::Exception of type 'undef'; or a pair of
+# values representing the exception type and info from which a
+# Template::Exception object is instantiated. e.g.
+#
+# $context->throw($exception);
+# $context->throw("I'm sorry Dave, I can't do that");
+# $context->throw('denied', "I'm sorry Dave, I can't do that");
+#
+# An optional third parameter can be supplied in the last case which
+# is a reference to the current output buffer containing the results
+# of processing the template up to the point at which the exception
+# was thrown. The RETURN and STOP directives, for example, use this
+# to propagate output back to the user, but it can safely be ignored
+# in most cases.
+#
+# This method rides on a one-way ticket to die() oblivion. It does not
+# return in any real sense of the word, but should get caught by a
+# surrounding eval { } block (e.g. a BLOCK or TRY) and handled
+# accordingly, or returned to the caller as an uncaught exception.
+#------------------------------------------------------------------------
+
+sub throw {
+ my ($self, $error, $info, $output) = @_;
+ local $" = ', ';
+
+ # die! die! die!
+ if (UNIVERSAL::isa($error, 'Template::Exception')) {
+ die $error;
+ }
+ elsif (defined $info) {
+ die (Template::Exception->new($error, $info, $output));
+ }
+ else {
+ $error ||= '';
+ die (Template::Exception->new('undef', $error, $output));
+ }
+
+ # not reached
+}
+
+
+#------------------------------------------------------------------------
+# catch($error, \$output)
+#
+# Called by various directives after catching an error thrown via die()
+# from within an eval { } block. The first parameter contains the errror
+# which may be a sanitized reference to a Template::Exception object
+# (such as that raised by the throw() method above, a plugin object,
+# and so on) or an error message thrown via die from somewhere in user
+# code. The latter are coerced into 'undef' Template::Exception objects.
+# Like throw() above, a reference to a scalar may be passed as an
+# additional parameter to represent the current output buffer
+# localised within the eval block. As exceptions are thrown upwards
+# and outwards from nested blocks, the catch() method reconstructs the
+# correct output buffer from these fragments, storing it in the
+# exception object for passing further onwards and upwards.
+#
+# Returns a reference to a Template::Exception object..
+#------------------------------------------------------------------------
+
+sub catch {
+ my ($self, $error, $output) = @_;
+
+ if (UNIVERSAL::isa($error, 'Template::Exception')) {
+ $error->text($output) if $output;
+ return $error;
+ }
+ else {
+ return Template::Exception->new('undef', $error, $output);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# localise(\%params)
+# delocalise()
+#
+# The localise() method creates a local copy of the current stash,
+# allowing the existing state of variables to be saved and later
+# restored via delocalise().
+#
+# A reference to a hash array may be passed containing local variable
+# definitions which should be added to the cloned namespace. These
+# values persist until delocalisation.
+#------------------------------------------------------------------------
+
+sub localise {
+ my $self = shift;
+ $self->{ STASH } = $self->{ STASH }->clone(@_);
+}
+
+sub delocalise {
+ my $self = shift;
+ $self->{ STASH } = $self->{ STASH }->declone();
+}
+
+
+#------------------------------------------------------------------------
+# visit($blocks)
+#
+# Each Template::Document calls the visit() method on the context
+# before processing itself. It passes a reference to the hash array
+# of named BLOCKs defined within the document, allowing them to be
+# added to the internal BLKSTACK list which is subsequently used by
+# template() to resolve templates.
+# from a provider.
+#------------------------------------------------------------------------
+
+sub visit {
+ my ($self, $blocks) = @_;
+ unshift(@{ $self->{ BLKSTACK } }, $blocks)
+}
+
+
+#------------------------------------------------------------------------
+# leave()
+#
+# The leave() method is called when the document has finished
+# processing itself. This removes the entry from the BLKSTACK list
+# that was added visit() above. For persistance of BLOCK definitions,
+# the process() method (i.e. the PROCESS directive) does some extra
+# magic to copy BLOCKs into a shared hash.
+#------------------------------------------------------------------------
+
+sub leave {
+ my $self = shift;
+ shift(@{ $self->{ BLKSTACK } });
+}
+
+
+#------------------------------------------------------------------------
+# define_block($name, $block)
+#
+# Adds a new BLOCK definition to the local BLOCKS cache. $block may
+# be specified as a reference to a sub-routine or Template::Document
+# object or as text which is compiled into a template. Returns a true
+# value (the $block reference or compiled block reference) if
+# succesful or undef on failure. Call error() to retrieve the
+# relevent error message (i.e. compilation failure).
+#------------------------------------------------------------------------
+
+sub define_block {
+ my ($self, $name, $block) = @_;
+ $block = $self->template(\$block)
+ || return undef
+ unless ref $block;
+ $self->{ BLOCKS }->{ $name } = $block;
+}
+
+
+#------------------------------------------------------------------------
+# define_filter($name, $filter, $is_dynamic)
+#
+# Adds a new FILTER definition to the local FILTER_CACHE.
+#------------------------------------------------------------------------
+
+sub define_filter {
+ my ($self, $name, $filter, $is_dynamic) = @_;
+ my ($result, $error);
+ $filter = [ $filter, 1 ] if $is_dynamic;
+
+ foreach my $provider (@{ $self->{ LOAD_FILTERS } }) {
+ ($result, $error) = $provider->store($name, $filter);
+ return 1 unless $error;
+ $self->throw(&Template::Constants::ERROR_FILTER, $result)
+ if $error == &Template::Constants::STATUS_ERROR;
+ }
+ $self->throw(&Template::Constants::ERROR_FILTER,
+ "FILTER providers declined to store filter $name");
+}
+
+
+#------------------------------------------------------------------------
+# reset()
+#
+# Reset the state of the internal BLOCKS hash to clear any BLOCK
+# definitions imported via the PROCESS directive. Any original
+# BLOCKS definitions passed to the constructor will be restored.
+#------------------------------------------------------------------------
+
+sub reset {
+ my ($self, $blocks) = @_;
+ $self->{ BLKSTACK } = [ ];
+ $self->{ BLOCKS } = { %{ $self->{ INIT_BLOCKS } } };
+}
+
+
+#------------------------------------------------------------------------
+# stash()
+#
+# Simple accessor methods to return the STASH values. This is likely
+# to be called quite often so we provide a direct method rather than
+# relying on the slower AUTOLOAD.
+#------------------------------------------------------------------------
+
+sub stash {
+ return $_[0]->{ STASH };
+}
+
+
+#------------------------------------------------------------------------
+# define_vmethod($type, $name, \&sub)
+#
+# Passes $type, $name, and &sub on to stash->define_vmethod().
+#------------------------------------------------------------------------
+sub define_vmethod {
+ my $self = shift;
+ $self->stash->define_vmethod(@_);
+}
+
+
+#------------------------------------------------------------------------
+# debugging($command, @args, \%params)
+#
+# Method for controlling the debugging status of the context. The first
+# argument can be 'on' or 'off' to enable/disable debugging, 'format'
+# to define the format of the debug message, or 'msg' to generate a
+# debugging message reporting the file, line, message text, etc.,
+# according to the current debug format.
+#------------------------------------------------------------------------
+
+sub debugging {
+ my $self = shift;
+ my $hash = ref $_[-1] eq 'HASH' ? pop : { };
+ my @args = @_;
+
+# print "*** debug(@args)\n";
+ if (@args) {
+ if ($args[0] =~ /^on|1$/i) {
+ $self->{ DEBUG_DIRS } = 1;
+ shift(@args);
+ }
+ elsif ($args[0] =~ /^off|0$/i) {
+ $self->{ DEBUG_DIRS } = 0;
+ shift(@args);
+ }
+ }
+
+ if (@args) {
+ if ($args[0] =~ /^msg$/i) {
+ return unless $self->{ DEBUG_DIRS };
+ my $format = $self->{ DEBUG_FORMAT };
+ $format = $DEBUG_FORMAT unless defined $format;
+ $format =~ s/\$(\w+)/$hash->{ $1 }/ge;
+ return $format;
+ }
+ elsif ($args[0] =~ /^format$/i) {
+ $self->{ DEBUG_FORMAT } = $args[1];
+ }
+ # else ignore
+ }
+
+ return '';
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# Provides pseudo-methods for read-only access to various internal
+# members. For example, templates(), plugins(), filters(),
+# eval_perl(), load_perl(), etc. These aren't called very often, or
+# may never be called at all.
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+ my $result;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ warn "no such context method/member: $method\n"
+ unless defined ($result = $self->{ uc $method });
+
+ return $result;
+}
+
+
+#------------------------------------------------------------------------
+# DESTROY
+#
+# Stash may contain references back to the Context via macro closures,
+# etc. This breaks the circular references.
+#------------------------------------------------------------------------
+
+sub DESTROY {
+ my $self = shift;
+ undef $self->{ STASH };
+}
+
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# _init(\%config)
+#
+# Initialisation method called by Template::Base::new()
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $config) = @_;
+ my ($name, $item, $method, $block, $blocks);
+ my @itemlut = (
+ LOAD_TEMPLATES => 'provider',
+ LOAD_PLUGINS => 'plugins',
+ LOAD_FILTERS => 'filters'
+ );
+
+ # LOAD_TEMPLATE, LOAD_PLUGINS, LOAD_FILTERS - lists of providers
+ while (($name, $method) = splice(@itemlut, 0, 2)) {
+ $item = $config->{ $name }
+ || Template::Config->$method($config)
+ || return $self->error($Template::Config::ERROR);
+ $self->{ $name } = ref $item eq 'ARRAY' ? $item : [ $item ];
+ }
+
+ my $providers = $self->{ LOAD_TEMPLATES };
+ my $prefix_map = $self->{ PREFIX_MAP } = $config->{ PREFIX_MAP } || { };
+ while (my ($key, $val) = each %$prefix_map) {
+ $prefix_map->{ $key } = [ ref $val ? $val :
+ map { $providers->[$_] }
+ split(/\D+/, $val) ]
+ unless ref $val eq 'ARRAY';
+# print(STDERR "prefix $key => $val => [",
+# join(', ', @{ $prefix_map->{ $key } }), "]\n");
+ }
+
+ # STASH
+ $self->{ STASH } = $config->{ STASH } || do {
+ my $predefs = $config->{ VARIABLES }
+ || $config->{ PRE_DEFINE }
+ || { };
+
+ # hack to get stash to know about debug mode
+ $predefs->{ _DEBUG } = ( ($config->{ DEBUG } || 0)
+ & &Template::Constants::DEBUG_UNDEF ) ? 1 : 0
+ unless defined $predefs->{ _DEBUG };
+
+ Template::Config->stash($predefs)
+ || return $self->error($Template::Config::ERROR);
+ };
+
+ # compile any template BLOCKS specified as text
+ $blocks = $config->{ BLOCKS } || { };
+ $self->{ INIT_BLOCKS } = $self->{ BLOCKS } = {
+ map {
+ $block = $blocks->{ $_ };
+ $block = $self->template(\$block)
+ || return undef
+ unless ref $block;
+ ($_ => $block);
+ }
+ keys %$blocks
+ };
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # RECURSION - flag indicating is recursion into templates is supported
+ # EVAL_PERL - flag indicating if PERL blocks should be processed
+ # TRIM - flag to remove leading and trailing whitespace from output
+ # BLKSTACK - list of hashes of BLOCKs defined in current template(s)
+ # CONFIG - original configuration hash
+ # EXPOSE_BLOCKS - make blocks visible as pseudo-files
+ # DEBUG_FORMAT - format for generating template runtime debugging messages
+ # DEBUG - format for generating template runtime debugging messages
+
+ $self->{ RECURSION } = $config->{ RECURSION } || 0;
+ $self->{ EVAL_PERL } = $config->{ EVAL_PERL } || 0;
+ $self->{ TRIM } = $config->{ TRIM } || 0;
+ $self->{ BLKSTACK } = [ ];
+ $self->{ CONFIG } = $config;
+ $self->{ EXPOSE_BLOCKS } = defined $config->{ EXPOSE_BLOCKS }
+ ? $config->{ EXPOSE_BLOCKS }
+ : 0;
+
+ $self->{ DEBUG_FORMAT } = $config->{ DEBUG_FORMAT };
+ $self->{ DEBUG_DIRS } = ($config->{ DEBUG } || 0)
+ & Template::Constants::DEBUG_DIRS;
+ $self->{ DEBUG } = defined $config->{ DEBUG }
+ ? $config->{ DEBUG } & ( Template::Constants::DEBUG_CONTEXT
+ | Template::Constants::DEBUG_FLAGS )
+ : $DEBUG;
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal state
+# of the context object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $output = "[Template::Context] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ foreach $key (qw( RECURSION EVAL_PERL TRIM )) {
+ $output .= sprintf($format, $key, $self->{ $key });
+ }
+ foreach my $pname (qw( LOAD_TEMPLATES LOAD_PLUGINS LOAD_FILTERS )) {
+ my $provtext = "[\n";
+ foreach my $prov (@{ $self->{ $pname } }) {
+ $provtext .= $prov->_dump();
+# $provtext .= ",\n";
+ }
+ $provtext =~ s/\n/\n /g;
+ $provtext =~ s/\s+$//;
+ $provtext .= ",\n ]";
+ $output .= sprintf($format, $pname, $provtext);
+ }
+ $output .= sprintf($format, STASH => $self->{ STASH }->_dump());
+ $output .= '}';
+ return $output;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Context - Runtime context in which templates are processed
+
+=head1 SYNOPSIS
+
+ use Template::Context;
+
+ # constructor
+ $context = Template::Context->new(\%config)
+ || die $Template::Context::ERROR;
+
+ # fetch (load and compile) a template
+ $template = $context->template($template_name);
+
+ # fetch (load and instantiate) a plugin object
+ $plugin = $context->plugin($name, \@args);
+
+ # fetch (return or create) a filter subroutine
+ $filter = $context->filter($name, \@args, $alias);
+
+ # process/include a template, errors are thrown via die()
+ $output = $context->process($template, \%vars);
+ $output = $context->include($template, \%vars);
+
+ # raise an exception via die()
+ $context->throw($error_type, $error_message, \$output_buffer);
+
+ # catch an exception, clean it up and fix output buffer
+ $exception = $context->catch($exception, \$output_buffer);
+
+ # save/restore the stash to effect variable localisation
+ $new_stash = $context->localise(\%vars);
+ $old_stash = $context->delocalise();
+
+ # add new BLOCK or FILTER definitions
+ $context->define_block($name, $block);
+ $context->define_filter($name, \&filtersub, $is_dynamic);
+
+ # reset context, clearing any imported BLOCK definitions
+ $context->reset();
+
+ # methods for accessing internal items
+ $stash = $context->stash();
+ $tflag = $context->trim();
+ $epflag = $context->eval_perl();
+ $providers = $context->templates();
+ $providers = $context->plugins();
+ $providers = $context->filters();
+ ...
+
+=head1 DESCRIPTION
+
+The Template::Context module defines an object class for representing
+a runtime context in which templates are processed. It provides an
+interface to the fundamental operations of the Template Toolkit
+processing engine through which compiled templates (i.e. Perl code
+constructed from the template source) can process templates, load
+plugins and filters, raise exceptions and so on.
+
+A default Template::Context object is created by the Template module.
+Any Template::Context options may be passed to the Template new()
+constructor method and will be forwarded to the Template::Context
+constructor.
+
+ use Template;
+
+ my $template = Template->new({
+ TRIM => 1,
+ EVAL_PERL => 1,
+ BLOCKS => {
+ header => 'This is the header',
+ footer => 'This is the footer',
+ },
+ });
+
+Similarly, the Template::Context constructor will forward all configuration
+parameters onto other default objects (e.g. Template::Provider, Template::Plugins,
+Template::Filters, etc.) that it may need to instantiate.
+
+ $context = Template::Context->new({
+ INCLUDE_PATH => '/home/abw/templates', # provider option
+ TAG_STYLE => 'html', # parser option
+ });
+
+A Template::Context object (or subclass/derivative) can be explicitly
+instantiated and passed to the Template new() constructor method as
+the CONTEXT item.
+
+ use Template;
+ use Template::Context;
+
+ my $context = Template::Context->new({ TRIM => 1 });
+ my $template = Template->new({ CONTEXT => $context });
+
+The Template module uses the Template::Config context() factory method
+to create a default context object when required. The
+$Template::Config::CONTEXT package variable may be set to specify an
+alternate context module. This will be loaded automatically and its
+new() constructor method called by the context() factory method when
+a default context object is required.
+
+ use Template;
+
+ $Template::Config::CONTEXT = 'MyOrg::Template::Context';
+
+ my $template = Template->new({
+ EVAL_PERL => 1,
+ EXTRA_MAGIC => 'red hot', # your extra config items
+ ...
+ });
+
+=head1 METHODS
+
+=head2 new(\%params)
+
+The new() constructor method is called to instantiate a Template::Context
+object. Configuration parameters may be specified as a HASH reference or
+as a list of (name =E<gt> value) pairs.
+
+ my $context = Template::Context->new({
+ INCLUDE_PATH => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+ my $context = Template::Context->new( EVAL_PERL => 1 );
+
+The new() method returns a Template::Context object (or sub-class) or
+undef on error. In the latter case, a relevant error message can be
+retrieved by the error() class method or directly from the
+$Template::Context::ERROR package variable.
+
+ my $context = Template::Context->new(\%config)
+ || die Template::Context->error();
+
+ my $context = Template::Context->new(\%config)
+ || die $Template::Context::ERROR;
+
+The following configuration items may be specified.
+
+=over 4
+
+
+=item VARIABLES, PRE_DEFINE
+
+The VARIABLES option (or PRE_DEFINE - they're equivalent) can be used
+to specify a hash array of template variables that should be used to
+pre-initialise the stash when it is created. These items are ignored
+if the STASH item is defined.
+
+ my $context = Template::Context->new({
+ VARIABLES => {
+ title => 'A Demo Page',
+ author => 'Joe Random Hacker',
+ version => 3.14,
+ },
+ };
+
+or
+
+ my $context = Template::Context->new({
+ PRE_DEFINE => {
+ title => 'A Demo Page',
+ author => 'Joe Random Hacker',
+ version => 3.14,
+ },
+ };
+
+
+
+
+
+=item BLOCKS
+
+The BLOCKS option can be used to pre-define a default set of template
+blocks. These should be specified as a reference to a hash array
+mapping template names to template text, subroutines or Template::Document
+objects.
+
+ my $context = Template::Context->new({
+ BLOCKS => {
+ header => 'The Header. [% title %]',
+ footer => sub { return $some_output_text },
+ another => Template::Document->new({ ... }),
+ },
+ });
+
+
+
+
+
+=item TRIM
+
+The TRIM option can be set to have any leading and trailing whitespace
+automatically removed from the output of all template files and BLOCKs.
+
+By example, the following BLOCK definition
+
+ [% BLOCK foo %]
+ Line 1 of foo
+ [% END %]
+
+will be processed is as "\nLine 1 of foo\n". When INCLUDEd, the surrounding
+newlines will also be introduced.
+
+ before
+ [% INCLUDE foo %]
+ after
+
+output:
+ before
+
+ Line 1 of foo
+
+ after
+
+With the TRIM option set to any true value, the leading and trailing
+newlines (which count as whitespace) will be removed from the output
+of the BLOCK.
+
+ before
+ Line 1 of foo
+ after
+
+The TRIM option is disabled (0) by default.
+
+
+
+
+
+
+=item EVAL_PERL
+
+This flag is used to indicate if PERL and/or RAWPERL blocks should be
+evaluated. By default, it is disabled and any PERL or RAWPERL blocks
+encountered will raise exceptions of type 'perl' with the message
+'EVAL_PERL not set'. Note however that any RAWPERL blocks should
+always contain valid Perl code, regardless of the EVAL_PERL flag. The
+parser will fail to compile templates that contain invalid Perl code
+in RAWPERL blocks and will throw a 'file' exception.
+
+When using compiled templates (see
+L<COMPILE_EXT|Template::Manual::Config/Caching_and_Compiling_Options> and
+L<COMPILE_DIR|Template::Manual::Config/Caching_and_Compiling_Options>),
+the EVAL_PERL has an affect when the template is compiled, and again
+when the templates is subsequently processed, possibly in a different
+context to the one that compiled it.
+
+If the EVAL_PERL is set when a template is compiled, then all PERL and
+RAWPERL blocks will be included in the compiled template. If the
+EVAL_PERL option isn't set, then Perl code will be generated which
+B<always> throws a 'perl' exception with the message 'EVAL_PERL not
+set' B<whenever> the compiled template code is run.
+
+Thus, you must have EVAL_PERL set if you want your compiled templates
+to include PERL and RAWPERL blocks.
+
+At some point in the future, using a different invocation of the
+Template Toolkit, you may come to process such a pre-compiled
+template. Assuming the EVAL_PERL option was set at the time the
+template was compiled, then the output of any RAWPERL blocks will be
+included in the compiled template and will get executed when the
+template is processed. This will happen regardless of the runtime
+EVAL_PERL status.
+
+Regular PERL blocks are a little more cautious, however. If the
+EVAL_PERL flag isn't set for the I<current> context, that is, the
+one which is trying to process it, then it will throw the familiar 'perl'
+exception with the message, 'EVAL_PERL not set'.
+
+Thus you can compile templates to include PERL blocks, but optionally
+disable them when you process them later. Note however that it is
+possible for a PERL block to contain a Perl "BEGIN { # some code }"
+block which will always get run regardless of the runtime EVAL_PERL
+status. Thus, if you set EVAL_PERL when compiling templates, it is
+assumed that you trust the templates to Do The Right Thing. Otherwise
+you must accept the fact that there's no bulletproof way to prevent
+any included code from trampling around in the living room of the
+runtime environment, making a real nuisance of itself if it really
+wants to. If you don't like the idea of such uninvited guests causing
+a bother, then you can accept the default and keep EVAL_PERL disabled.
+
+
+
+
+
+
+
+=item RECURSION
+
+The template processor will raise a file exception if it detects
+direct or indirect recursion into a template. Setting this option to
+any true value will allow templates to include each other recursively.
+
+
+
+=item LOAD_TEMPLATES
+
+The LOAD_TEMPLATE option can be used to provide a reference to a list
+of Template::Provider objects or sub-classes thereof which will take
+responsibility for loading and compiling templates.
+
+ my $context = Template::Context->new({
+ LOAD_TEMPLATES => [
+ MyOrg::Template::Provider->new({ ... }),
+ Template::Provider->new({ ... }),
+ ],
+ });
+
+When a PROCESS, INCLUDE or WRAPPER directive is encountered, the named
+template may refer to a locally defined BLOCK or a file relative to
+the INCLUDE_PATH (or an absolute or relative path if the appropriate
+ABSOLUTE or RELATIVE options are set). If a BLOCK definition can't be
+found (see the Template::Context template() method for a discussion of
+BLOCK locality) then each of the LOAD_TEMPLATES provider objects is
+queried in turn via the fetch() method to see if it can supply the
+required template. Each provider can return a compiled template, an
+error, or decline to service the request in which case the
+responsibility is passed to the next provider. If none of the
+providers can service the request then a 'not found' error is
+returned. The same basic provider mechanism is also used for the
+INSERT directive but it bypasses any BLOCK definitions and doesn't
+attempt is to parse or process the contents of the template file.
+
+This is an implementation of the 'Chain of Responsibility'
+design pattern as described in
+"Design Patterns", Erich Gamma, Richard Helm, Ralph Johnson, John
+Vlissides), Addision-Wesley, ISBN 0-201-63361-2, page 223
+.
+
+If LOAD_TEMPLATES is undefined, a single default provider will be
+instantiated using the current configuration parameters. For example,
+the Template::Provider INCLUDE_PATH option can be specified in the Template::Context configuration and will be correctly passed to the provider's
+constructor method.
+
+ my $context = Template::Context->new({
+ INCLUDE_PATH => '/here:/there',
+ });
+
+
+
+
+
+=item LOAD_PLUGINS
+
+The LOAD_PLUGINS options can be used to specify a list of provider
+objects (i.e. they implement the fetch() method) which are responsible
+for loading and instantiating template plugin objects. The
+Template::Content plugin() method queries each provider in turn in a
+"Chain of Responsibility" as per the template() and filter() methods.
+
+ my $context = Template::Context->new({
+ LOAD_PLUGINS => [
+ MyOrg::Template::Plugins->new({ ... }),
+ Template::Plugins->new({ ... }),
+ ],
+ });
+
+By default, a single Template::Plugins object is created using the
+current configuration hash. Configuration items destined for the
+Template::Plugins constructor may be added to the Template::Context
+constructor.
+
+ my $context = Template::Context->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugins',
+ LOAD_PERL => 1,
+ });
+
+
+
+
+
+=item LOAD_FILTERS
+
+The LOAD_FILTERS option can be used to specify a list of provider
+objects (i.e. they implement the fetch() method) which are responsible
+for returning and/or creating filter subroutines. The
+Template::Context filter() method queries each provider in turn in a
+"Chain of Responsibility" as per the template() and plugin() methods.
+
+ my $context = Template::Context->new({
+ LOAD_FILTERS => [
+ MyTemplate::Filters->new(),
+ Template::Filters->new(),
+ ],
+ });
+
+By default, a single Template::Filters object is created for the
+LOAD_FILTERS list.
+
+
+
+=item STASH
+
+A reference to a Template::Stash object or sub-class which will take
+responsibility for managing template variables.
+
+ my $stash = MyOrg::Template::Stash->new({ ... });
+ my $context = Template::Context->new({
+ STASH => $stash,
+ });
+
+If unspecified, a default stash object is created using the VARIABLES
+configuration item to initialise the stash variables. These may also
+be specified as the PRE_DEFINE option for backwards compatibility with
+version 1.
+
+ my $context = Template::Context->new({
+ VARIABLES => {
+ id => 'abw',
+ name => 'Andy Wardley',
+ },
+ };
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable various debugging features
+of the Template::Context module.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_CONTEXT | DEBUG_DIRS,
+ });
+
+The DEBUG value can include any of the following. Multiple values
+should be combined using the logical OR operator, '|'.
+
+=over 4
+
+=item DEBUG_CONTEXT
+
+Enables general debugging messages for the
+L<Template::Context|Template::Context> module.
+
+=item DEBUG_DIRS
+
+This option causes the Template Toolkit to generate comments
+indicating the source file, line and original text of each directive
+in the template. These comments are embedded in the template output
+using the format defined in the DEBUG_FORMAT configuration item, or a
+simple default format if unspecified.
+
+For example, the following template fragment:
+
+
+ Hello World
+
+would generate this output:
+
+ ## input text line 1 : ##
+ Hello
+ ## input text line 2 : World ##
+ World
+
+
+=back
+
+
+
+
+
+=back
+
+=head2 template($name)
+
+Returns a compiled template by querying each of the LOAD_TEMPLATES providers
+(instances of Template::Provider, or sub-class) in turn.
+
+ $template = $context->template('header');
+
+On error, a Template::Exception object of type 'file' is thrown via
+die(). This can be caught by enclosing the call to template() in an
+eval block and examining $@.
+
+ eval {
+ $template = $context->template('header');
+ };
+ if ($@) {
+ print "failed to fetch template: $@\n";
+ }
+
+=head2 plugin($name, \@args)
+
+Instantiates a plugin object by querying each of the LOAD_PLUGINS
+providers. The default LOAD_PLUGINS provider is a Template::Plugins
+object which attempts to load plugin modules, according the various
+configuration items such as PLUGIN_BASE, LOAD_PERL, etc., and then
+instantiate an object via new(). A reference to a list of constructor
+arguments may be passed as the second parameter. These are forwarded
+to the plugin constructor.
+
+Returns a reference to a plugin (which is generally an object, but
+doesn't have to be). Errors are thrown as Template::Exception objects
+of type 'plugin'.
+
+ $plugin = $context->plugin('DBI', 'dbi:msql:mydbname');
+
+=head2 filter($name, \@args, $alias)
+
+Instantiates a filter subroutine by querying the LOAD_FILTERS providers.
+The default LOAD_FILTERS providers is a Template::Filters object.
+Additional arguments may be passed by list reference along with an
+optional alias under which the filter will be cached for subsequent
+use. The filter is cached under its own $name if $alias is undefined.
+Subsequent calls to filter($name) will return the cached entry, if
+defined. Specifying arguments bypasses the caching mechanism and
+always creates a new filter. Errors are thrown as Template::Exception
+objects of typre 'filter'.
+
+ # static filter (no args)
+ $filter = $context->filter('html');
+
+ # dynamic filter (args) aliased to 'padright'
+ $filter = $context->filter('format', '%60s', 'padright');
+
+ # retrieve previous filter via 'padright' alias
+ $filter = $context->filter('padright');
+
+=head2 process($template, \%vars)
+
+Processes a template named or referenced by the first parameter and returns
+the output generated. An optional reference to a hash array may be passed
+as the second parameter, containing variable definitions which will be set
+before the template is processed. The template is processed in the current
+context, with no localisation of variables performed. Errors are thrown
+as Template::Exception objects via die().
+
+ $output = $context->process('header', { title => 'Hello World' });
+
+=head2 include($template, \%vars)
+
+Similar to process() above, but using localised variables. Changes made to
+any variables will only persist until the include() method completes.
+
+ $output = $context->include('header', { title => 'Hello World' });
+
+=head2 throw($error_type, $error_message, \$output)
+
+Raises an exception in the form of a Template::Exception object by
+calling die(). This method may be passed a reference to an existing
+Template::Exception object; a single value containing an error message
+which is used to instantiate a Template::Exception of type 'undef'; or
+a pair of values representing the exception type and info from which a
+Template::Exception object is instantiated. e.g.
+
+ $context->throw($exception);
+ $context->throw("I'm sorry Dave, I can't do that");
+ $context->throw('denied', "I'm sorry Dave, I can't do that");
+
+The optional third parameter may be a reference to the current output
+buffer. This is then stored in the exception object when created,
+allowing the catcher to examine and use the output up to the point at
+which the exception was raised.
+
+ $output .= 'blah blah blah';
+ $output .= 'more rhubarb';
+ $context->throw('yack', 'Too much yacking', \$output);
+
+=head2 catch($exception, \$output)
+
+Catches an exception thrown, either as a reference to a
+Template::Exception object or some other value. In the latter case,
+the error string is promoted to a Template::Exception object of
+'undef' type. This method also accepts a reference to the current
+output buffer which is passed to the Template::Exception constructor,
+or is appended to the output buffer stored in an existing
+Template::Exception object, if unique (i.e. not the same reference).
+By this process, the correct state of the output buffer can be
+reconstructed for simple or nested throws.
+
+=head2 define_block($name, $block)
+
+Adds a new block definition to the internal BLOCKS cache. The first
+argument should contain the name of the block and the second a reference
+to a Template::Document object or template sub-routine, or template text
+which is automatically compiled into a template sub-routine. Returns
+a true value (the sub-routine or Template::Document reference) on
+success or undef on failure. The relevant error message can be
+retrieved by calling the error() method.
+
+=head2 define_filter($name, \&filter, $is_dynamic)
+
+Adds a new filter definition by calling the store() method on each of
+the LOAD_FILTERS providers until accepted (in the usual case, this is
+accepted straight away by the one and only Template::Filters
+provider). The first argument should contain the name of the filter
+and the second a reference to a filter subroutine. The optional
+third argument can be set to any true value to indicate that the
+subroutine is a dynamic filter factory. Returns a true value or
+throws a 'filter' exception on error.
+
+=head2 localise(\%vars)
+
+Clones the stash to create a context with localised variables. Returns a
+reference to the newly cloned stash object which is also stored
+internally.
+
+ $stash = $context->localise();
+
+=head2 delocalise()
+
+Restore the stash to its state prior to localisation.
+
+ $stash = $context->delocalise();
+
+=head2 visit(\%blocks)
+
+This method is called by Template::Document objects immediately before
+they process their content. It is called to register any local BLOCK
+definitions with the context object so that they may be subsequently
+delivered on request.
+
+=head2 leave()
+
+Compliment to visit(), above. Called by Template::Document objects
+immediately after they process their content.
+
+=head2 reset()
+
+Clears the local BLOCKS cache of any BLOCK definitions. Any initial set of
+BLOCKS specified as a configuration item to the constructor will be reinstated.
+
+=head2 AUTOLOAD
+
+An AUTOLOAD method provides access to context configuration items.
+
+ $stash = $context->stash();
+ $tflag = $context->trim();
+ $epflag = $context->eval_perl();
+ ...
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.81, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Document|Template::Document>, L<Template::Exception|Template::Exception>, L<Template::Filters|Template::Filters>, L<Template::Plugins|Template::Plugins>, L<Template::Provider|Template::Provider>, L<Template::Service|Template::Service>, L<Template::Stash|Template::Stash>
diff --git a/lib/Template/Directive.pm b/lib/Template/Directive.pm
new file mode 100644
index 0000000..67982d3
--- /dev/null
+++ b/lib/Template/Directive.pm
@@ -0,0 +1,1004 @@
+#================================================================= -*-Perl-*-
+#
+# Template::Directive
+#
+# DESCRIPTION
+# Factory module for constructing templates from Perl code.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# WARNING
+# Much of this module is hairy, even furry in places. It needs
+# a lot of tidying up and may even be moved into a different place
+# altogether. The generator code is often inefficient, particulary in
+# being very anal about pretty-printing the Perl code all neatly, but
+# at the moment, that's still high priority for the sake of easier
+# debugging.
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Directive.pm,v 2.17 2002/08/08 11:59:15 abw Exp $
+#
+#============================================================================
+
+package Template::Directive;
+
+require 5.004;
+
+use strict;
+use Template::Base;
+use Template::Constants;
+use Template::Exception;
+
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG $PRETTY $WHILE_MAX );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.17 $ =~ /(\d+)\.(\d+)/);
+
+$WHILE_MAX = 1000 unless defined $WHILE_MAX;
+$PRETTY = 0 unless defined $PRETTY;
+my $OUTPUT = '$output .= ';
+
+
+sub _init {
+ my ($self, $config) = @_;
+ $self->{ NAMESPACE } = $config->{ NAMESPACE };
+ return $self;
+}
+
+
+sub pad {
+ my ($text, $pad) = @_;
+ $pad = ' ' x ($pad * 4);
+ $text =~ s/^(?!#line)/$pad/gm;
+ $text;
+}
+
+#========================================================================
+# FACTORY METHODS
+#
+# These methods are called by the parser to construct directive instances.
+#========================================================================
+
+#------------------------------------------------------------------------
+# template($block)
+#------------------------------------------------------------------------
+
+sub template {
+ my ($class, $block) = @_;
+ $block = pad($block, 2) if $PRETTY;
+
+ return "sub { return '' }" unless $block =~ /\S/;
+
+ return <<EOF;
+sub {
+ my \$context = shift || die "template sub called without context\\n";
+ my \$stash = \$context->stash;
+ my \$output = '';
+ my \$error;
+
+ eval { BLOCK: {
+$block
+ } };
+ if (\$@) {
+ \$error = \$context->catch(\$@, \\\$output);
+ die \$error unless \$error->type eq 'return';
+ }
+
+ return \$output;
+}
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# anon_block($block) [% BLOCK %] ... [% END %]
+#------------------------------------------------------------------------
+
+sub anon_block {
+ my ($class, $block) = @_;
+ $block = pad($block, 2) if $PRETTY;
+
+ return <<EOF;
+
+# BLOCK
+$OUTPUT do {
+ my \$output = '';
+ my \$error;
+
+ eval { BLOCK: {
+$block
+ } };
+ if (\$@) {
+ \$error = \$context->catch(\$@, \\\$output);
+ die \$error unless \$error->type eq 'return';
+ }
+
+ \$output;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# block($blocktext)
+#------------------------------------------------------------------------
+
+sub block {
+ my ($class, $block) = @_;
+ return join("\n", @{ $block || [] });
+}
+
+
+#------------------------------------------------------------------------
+# textblock($text)
+#------------------------------------------------------------------------
+
+sub textblock {
+ my ($class, $text) = @_;
+ return "$OUTPUT " . &text($class, $text) . ';';
+}
+
+
+#------------------------------------------------------------------------
+# text($text)
+#------------------------------------------------------------------------
+
+sub text {
+ my ($class, $text) = @_;
+ for ($text) {
+ s/(["\$\@\\])/\\$1/g;
+ s/\n/\\n/g;
+ }
+ return '"' . $text . '"';
+}
+
+
+#------------------------------------------------------------------------
+# quoted(\@items) "foo$bar"
+#------------------------------------------------------------------------
+
+sub quoted {
+ my ($class, $items) = @_;
+ return '' unless @$items;
+ return ("('' . " . $items->[0] . ')') if scalar @$items == 1;
+ return '(' . join(' . ', @$items) . ')';
+# my $r = '(' . join(' . ', @$items) . ' . "")';
+# print STDERR "[$r]\n";
+# return $r;
+}
+
+
+#------------------------------------------------------------------------
+# ident(\@ident) foo.bar(baz)
+#------------------------------------------------------------------------
+
+sub ident {
+ my ($class, $ident) = @_;
+ return "''" unless @$ident;
+ my $ns;
+
+ # does the first element of the identifier have a NAMESPACE
+ # handler defined?
+ if (ref $class && @$ident > 2 && ($ns = $class->{ NAMESPACE })) {
+ my $key = $ident->[0];
+ $key =~ s/^'(.+)'$/$1/s;
+ if ($ns = $ns->{ $key }) {
+ return $ns->ident($ident);
+ }
+ }
+
+ if (scalar @$ident <= 2 && ! $ident->[1]) {
+ $ident = $ident->[0];
+ }
+ else {
+ $ident = '[' . join(', ', @$ident) . ']';
+ }
+ return "\$stash->get($ident)";
+}
+
+#------------------------------------------------------------------------
+# identref(\@ident) \foo.bar(baz)
+#------------------------------------------------------------------------
+
+sub identref {
+ my ($class, $ident) = @_;
+ return "''" unless @$ident;
+ if (scalar @$ident <= 2 && ! $ident->[1]) {
+ $ident = $ident->[0];
+ }
+ else {
+ $ident = '[' . join(', ', @$ident) . ']';
+ }
+ return "\$stash->getref($ident)";
+}
+
+
+#------------------------------------------------------------------------
+# assign(\@ident, $value, $default) foo = bar
+#------------------------------------------------------------------------
+
+sub assign {
+ my ($class, $var, $val, $default) = @_;
+
+ if (ref $var) {
+ if (scalar @$var == 2 && ! $var->[1]) {
+ $var = $var->[0];
+ }
+ else {
+ $var = '[' . join(', ', @$var) . ']';
+ }
+ }
+ $val .= ', 1' if $default;
+ return "\$stash->set($var, $val)";
+}
+
+
+#------------------------------------------------------------------------
+# args(\@args) foo, bar, baz = qux
+#------------------------------------------------------------------------
+
+sub args {
+ my ($class, $args) = @_;
+ my $hash = shift @$args;
+ push(@$args, '{ ' . join(', ', @$hash) . ' }')
+ if @$hash;
+
+ return '0' unless @$args;
+ return '[ ' . join(', ', @$args) . ' ]';
+}
+
+#------------------------------------------------------------------------
+# filenames(\@names)
+#------------------------------------------------------------------------
+
+sub filenames {
+ my ($class, $names) = @_;
+ if (@$names > 1) {
+ $names = '[ ' . join(', ', @$names) . ' ]';
+ }
+ else {
+ $names = shift @$names;
+ }
+ return $names;
+}
+
+
+#------------------------------------------------------------------------
+# get($expr) [% foo %]
+#------------------------------------------------------------------------
+
+sub get {
+ my ($class, $expr) = @_;
+ return "$OUTPUT $expr;";
+}
+
+
+#------------------------------------------------------------------------
+# call($expr) [% CALL bar %]
+#------------------------------------------------------------------------
+
+sub call {
+ my ($class, $expr) = @_;
+ $expr .= ';';
+ return $expr;
+}
+
+
+#------------------------------------------------------------------------
+# set(\@setlist) [% foo = bar, baz = qux %]
+#------------------------------------------------------------------------
+
+sub set {
+ my ($class, $setlist) = @_;
+ my $output;
+ while (my ($var, $val) = splice(@$setlist, 0, 2)) {
+ $output .= &assign($class, $var, $val) . ";\n";
+ }
+ chomp $output;
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# default(\@setlist) [% DEFAULT foo = bar, baz = qux %]
+#------------------------------------------------------------------------
+
+sub default {
+ my ($class, $setlist) = @_;
+ my $output;
+ while (my ($var, $val) = splice(@$setlist, 0, 2)) {
+ $output .= &assign($class, $var, $val, 1) . ";\n";
+ }
+ chomp $output;
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# insert(\@nameargs) [% INSERT file %]
+# # => [ [ $file, ... ], \@args ]
+#------------------------------------------------------------------------
+
+sub insert {
+ my ($class, $nameargs) = @_;
+ my ($file, $args) = @$nameargs;
+ $file = $class->filenames($file);
+ return "$OUTPUT \$context->insert($file);";
+}
+
+
+#------------------------------------------------------------------------
+# include(\@nameargs) [% INCLUDE template foo = bar %]
+# # => [ [ $file, ... ], \@args ]
+#------------------------------------------------------------------------
+
+sub include {
+ my ($class, $nameargs) = @_;
+ my ($file, $args) = @$nameargs;
+ my $hash = shift @$args;
+ $file = $class->filenames($file);
+ $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+ return "$OUTPUT \$context->include($file);";
+}
+
+
+#------------------------------------------------------------------------
+# process(\@nameargs) [% PROCESS template foo = bar %]
+# # => [ [ $file, ... ], \@args ]
+#------------------------------------------------------------------------
+
+sub process {
+ my ($class, $nameargs) = @_;
+ my ($file, $args) = @$nameargs;
+ my $hash = shift @$args;
+ $file = $class->filenames($file);
+ $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+ return "$OUTPUT \$context->process($file);";
+}
+
+
+#------------------------------------------------------------------------
+# if($expr, $block, $else) [% IF foo < bar %]
+# ...
+# [% ELSE %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub if {
+ my ($class, $expr, $block, $else) = @_;
+ my @else = $else ? @$else : ();
+ $else = pop @else;
+ $block = pad($block, 1) if $PRETTY;
+
+ my $output = "if ($expr) {\n$block\n}\n";
+
+ foreach my $elsif (@else) {
+ ($expr, $block) = @$elsif;
+ $block = pad($block, 1) if $PRETTY;
+ $output .= "elsif ($expr) {\n$block\n}\n";
+ }
+ if (defined $else) {
+ $else = pad($else, 1) if $PRETTY;
+ $output .= "else {\n$else\n}\n";
+ }
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# foreach($target, $list, $args, $block) [% FOREACH x = [ foo bar ] %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub foreach {
+ my ($class, $target, $list, $args, $block) = @_;
+ $args = shift @$args;
+ $args = @$args ? ', { ' . join(', ', @$args) . ' }' : '';
+
+ my ($loop_save, $loop_set, $loop_restore, $setiter);
+ if ($target) {
+ $loop_save = 'eval { $oldloop = ' . &ident($class, ["'loop'"]) . ' }';
+ $loop_set = "\$stash->{'$target'} = \$value";
+ $loop_restore = "\$stash->set('loop', \$oldloop)";
+ }
+ else {
+ $loop_save = '$stash = $context->localise()';
+# $loop_set = "\$stash->set('import', \$value) "
+# . "if ref \$value eq 'HASH'";
+ $loop_set = "\$stash->get(['import', [\$value]]) "
+ . "if ref \$value eq 'HASH'";
+ $loop_restore = '$stash = $context->delocalise()';
+ }
+ $block = pad($block, 3) if $PRETTY;
+
+ return <<EOF;
+
+# FOREACH
+do {
+ my (\$value, \$error, \$oldloop);
+ my \$list = $list;
+
+ unless (UNIVERSAL::isa(\$list, 'Template::Iterator')) {
+ \$list = Template::Config->iterator(\$list)
+ || die \$Template::Config::ERROR, "\\n";
+ }
+
+ (\$value, \$error) = \$list->get_first();
+ $loop_save;
+ \$stash->set('loop', \$list);
+ eval {
+LOOP: while (! \$error) {
+ $loop_set;
+$block;
+ (\$value, \$error) = \$list->get_next();
+ }
+ };
+ $loop_restore;
+ die \$@ if \$@;
+ \$error = 0 if \$error && \$error eq Template::Constants::STATUS_DONE;
+ die \$error if \$error;
+};
+EOF
+}
+
+#------------------------------------------------------------------------
+# next() [% NEXT %]
+#
+# Next iteration of a FOREACH loop (experimental)
+#------------------------------------------------------------------------
+
+sub next {
+ return <<EOF;
+(\$value, \$error) = \$list->get_next();
+next LOOP;
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# wrapper(\@nameargs, $block) [% WRAPPER template foo = bar %]
+# # => [ [$file,...], \@args ]
+#------------------------------------------------------------------------
+
+sub wrapper {
+ my ($class, $nameargs, $block) = @_;
+ my ($file, $args) = @$nameargs;
+ my $hash = shift @$args;
+
+ local $" = ', ';
+# print STDERR "wrapper([@$file], { @$hash })\n";
+
+ return $class->multi_wrapper($file, $hash, $block)
+ if @$file > 1;
+ $file = shift @$file;
+
+ $block = pad($block, 1) if $PRETTY;
+ push(@$hash, "'content'", '$output');
+ $file .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+
+ return <<EOF;
+
+# WRAPPER
+$OUTPUT do {
+ my \$output = '';
+$block
+ \$context->include($file);
+};
+EOF
+}
+
+
+sub multi_wrapper {
+ my ($class, $file, $hash, $block) = @_;
+ $block = pad($block, 1) if $PRETTY;
+
+ push(@$hash, "'content'", '$output');
+ $hash = @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+
+ $file = join(', ', reverse @$file);
+# print STDERR "multi wrapper: $file\n";
+
+ return <<EOF;
+
+# WRAPPER
+$OUTPUT do {
+ my \$output = '';
+$block
+ foreach ($file) {
+ \$output = \$context->include(\$_$hash);
+ }
+ \$output;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# while($expr, $block) [% WHILE x < 10 %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub while {
+ my ($class, $expr, $block) = @_;
+ $block = pad($block, 2) if $PRETTY;
+
+ return <<EOF;
+
+# WHILE
+do {
+ my \$failsafe = $WHILE_MAX;
+LOOP:
+ while (--\$failsafe && ($expr)) {
+$block
+ }
+ die "WHILE loop terminated (> $WHILE_MAX iterations)\\n"
+ unless \$failsafe;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# switch($expr, \@case) [% SWITCH %]
+# [% CASE foo %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub switch {
+ my ($class, $expr, $case) = @_;
+ my @case = @$case;
+ my ($match, $block, $default);
+ my $caseblock = '';
+
+ $default = pop @case;
+
+ foreach $case (@case) {
+ $match = $case->[0];
+ $block = $case->[1];
+ $block = pad($block, 1) if $PRETTY;
+ $caseblock .= <<EOF;
+\$match = $match;
+\$match = [ \$match ] unless ref \$match eq 'ARRAY';
+if (grep(/^\$result\$/, \@\$match)) {
+$block
+ last SWITCH;
+}
+EOF
+ }
+
+ $caseblock .= $default
+ if defined $default;
+ $caseblock = pad($caseblock, 2) if $PRETTY;
+
+return <<EOF;
+
+# SWITCH
+do {
+ my \$result = $expr;
+ my \$match;
+ SWITCH: {
+$caseblock
+ }
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# try($block, \@catch) [% TRY %]
+# ...
+# [% CATCH %]
+# ...
+# [% END %]
+#------------------------------------------------------------------------
+
+sub try {
+ my ($class, $block, $catch) = @_;
+ my @catch = @$catch;
+ my ($match, $mblock, $default, $final, $n);
+ my $catchblock = '';
+ my $handlers = [];
+
+ $block = pad($block, 2) if $PRETTY;
+ $final = pop @catch;
+ $final = "# FINAL\n" . ($final ? "$final\n" : '')
+ . 'die $error if $error;' . "\n" . '$output;';
+ $final = pad($final, 1) if $PRETTY;
+
+ $n = 0;
+ foreach $catch (@catch) {
+ $match = $catch->[0] || do {
+ $default ||= $catch->[1];
+ next;
+ };
+ $mblock = $catch->[1];
+ $mblock = pad($mblock, 1) if $PRETTY;
+ push(@$handlers, "'$match'");
+ $catchblock .= $n++
+ ? "elsif (\$handler eq '$match') {\n$mblock\n}\n"
+ : "if (\$handler eq '$match') {\n$mblock\n}\n";
+ }
+ $catchblock .= "\$error = 0;";
+ $catchblock = pad($catchblock, 3) if $PRETTY;
+ if ($default) {
+ $default = pad($default, 1) if $PRETTY;
+ $default = "else {\n # DEFAULT\n$default\n \$error = '';\n}";
+ }
+ else {
+ $default = '# NO DEFAULT';
+ }
+ $default = pad($default, 2) if $PRETTY;
+
+ $handlers = join(', ', @$handlers);
+return <<EOF;
+
+# TRY
+$OUTPUT do {
+ my \$output = '';
+ my (\$error, \$handler);
+ eval {
+$block
+ };
+ if (\$@) {
+ \$error = \$context->catch(\$@, \\\$output);
+ die \$error if \$error->type =~ /^return|stop\$/;
+ \$stash->set('error', \$error);
+ \$stash->set('e', \$error);
+ if (defined (\$handler = \$error->select_handler($handlers))) {
+$catchblock
+ }
+$default
+ }
+$final
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# throw(\@nameargs) [% THROW foo "bar error" %]
+# # => [ [$type], \@args ]
+#------------------------------------------------------------------------
+
+sub throw {
+ my ($class, $nameargs) = @_;
+ my ($type, $args) = @$nameargs;
+ my $hash = shift(@$args);
+ my $info = shift(@$args);
+ $type = shift @$type; # uses same parser production as INCLUDE
+ # etc., which allow multiple names
+ # e.g. INCLUDE foo+bar+baz
+
+ if (! $info) {
+ $args = "$type, undef";
+ }
+ elsif (@$hash || @$args) {
+ local $" = ', ';
+ my $i = 0;
+ $args = "$type, { args => [ "
+ . join(', ', $info, @$args)
+ . ' ], '
+ . join(', ',
+ (map { "'" . $i++ . "' => $_" } ($info, @$args)),
+ @$hash)
+ . ' }';
+ }
+ else {
+ $args = "$type, $info";
+ }
+
+ return "\$context->throw($args, \\\$output);";
+}
+
+
+#------------------------------------------------------------------------
+# clear() [% CLEAR %]
+#
+# NOTE: this is redundant, being hard-coded (for now) into Parser.yp
+#------------------------------------------------------------------------
+
+sub clear {
+ return "\$output = '';";
+}
+
+#------------------------------------------------------------------------
+# break() [% BREAK %]
+#
+# NOTE: this is redundant, being hard-coded (for now) into Parser.yp
+#------------------------------------------------------------------------
+
+sub break {
+ return 'last LOOP;';
+}
+
+#------------------------------------------------------------------------
+# return() [% RETURN %]
+#------------------------------------------------------------------------
+
+sub return {
+ return "\$context->throw('return', '', \\\$output);";
+}
+
+#------------------------------------------------------------------------
+# stop() [% STOP %]
+#------------------------------------------------------------------------
+
+sub stop {
+ return "\$context->throw('stop', '', \\\$output);";
+}
+
+
+#------------------------------------------------------------------------
+# use(\@lnameargs) [% USE alias = plugin(args) %]
+# # => [ [$file, ...], \@args, $alias ]
+#------------------------------------------------------------------------
+
+sub use {
+ my ($class, $lnameargs) = @_;
+ my ($file, $args, $alias) = @$lnameargs;
+ $file = shift @$file; # same production rule as INCLUDE
+ $alias ||= $file;
+ $args = &args($class, $args);
+ $file .= ", $args" if $args;
+# my $set = &assign($class, $alias, '$plugin');
+ return "# USE\n"
+ . "\$stash->set($alias,\n"
+ . " \$context->plugin($file));";
+}
+
+#------------------------------------------------------------------------
+# view(\@nameargs, $block) [% VIEW name args %]
+# # => [ [$file, ... ], \@args ]
+#------------------------------------------------------------------------
+
+sub view {
+ my ($class, $nameargs, $block, $defblocks) = @_;
+ my ($name, $args) = @$nameargs;
+ my $hash = shift @$args;
+ $name = shift @$name; # same production rule as INCLUDE
+ $block = pad($block, 1) if $PRETTY;
+
+ if (%$defblocks) {
+ $defblocks = join(",\n", map { "'$_' => $defblocks->{ $_ }" }
+ keys %$defblocks);
+ $defblocks = pad($defblocks, 1) if $PRETTY;
+ $defblocks = "{\n$defblocks\n}";
+ push(@$hash, "'blocks'", $defblocks);
+ }
+ $hash = @$hash ? '{ ' . join(', ', @$hash) . ' }' : '';
+
+ return <<EOF;
+# VIEW
+do {
+ my \$output = '';
+ my \$oldv = \$stash->get('view');
+ my \$view = \$context->view($hash);
+ \$stash->set($name, \$view);
+ \$stash->set('view', \$view);
+
+$block
+
+ \$stash->set('view', \$oldv);
+ \$view->seal();
+ \$output;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# perl($block)
+#------------------------------------------------------------------------
+
+sub perl {
+ my ($class, $block) = @_;
+ $block = pad($block, 1) if $PRETTY;
+
+ return <<EOF;
+
+# PERL
+\$context->throw('perl', 'EVAL_PERL not set')
+ unless \$context->eval_perl();
+
+$OUTPUT do {
+ my \$output = "package Template::Perl;\\n";
+
+$block
+
+ local(\$Template::Perl::context) = \$context;
+ local(\$Template::Perl::stash) = \$stash;
+
+ my \$result = '';
+ tie *Template::Perl::PERLOUT, 'Template::TieString', \\\$result;
+ my \$save_stdout = select *Template::Perl::PERLOUT;
+
+ eval \$output;
+ select \$save_stdout;
+ \$context->throw(\$@) if \$@;
+ \$result;
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# no_perl()
+#------------------------------------------------------------------------
+
+sub no_perl {
+ my $class = shift;
+ return "\$context->throw('perl', 'EVAL_PERL not set');";
+}
+
+
+#------------------------------------------------------------------------
+# rawperl($block)
+#
+# NOTE: perhaps test context EVAL_PERL switch at compile time rather than
+# runtime?
+#------------------------------------------------------------------------
+
+sub rawperl {
+ my ($class, $block, $line) = @_;
+ for ($block) {
+ s/^\n+//;
+ s/\n+$//;
+ }
+ $block = pad($block, 1) if $PRETTY;
+ $line = $line ? " (starting line $line)" : '';
+
+ return <<EOF;
+# RAWPERL
+#line 1 "RAWPERL block$line"
+$block
+EOF
+}
+
+
+
+#------------------------------------------------------------------------
+# filter()
+#------------------------------------------------------------------------
+
+sub filter {
+ my ($class, $lnameargs, $block) = @_;
+ my ($name, $args, $alias) = @$lnameargs;
+ $name = shift @$name;
+ $args = &args($class, $args);
+ $args = $args ? "$args, $alias" : ", undef, $alias"
+ if $alias;
+ $name .= ", $args" if $args;
+ $block = pad($block, 1) if $PRETTY;
+
+ return <<EOF;
+
+# FILTER
+$OUTPUT do {
+ my \$output = '';
+ my \$filter = \$context->filter($name)
+ || \$context->throw(\$context->error);
+
+$block
+
+ &\$filter(\$output);
+};
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# capture($name, $block)
+#------------------------------------------------------------------------
+
+sub capture {
+ my ($class, $name, $block) = @_;
+
+ if (ref $name) {
+ if (scalar @$name == 2 && ! $name->[1]) {
+ $name = $name->[0];
+ }
+ else {
+ $name = '[' . join(', ', @$name) . ']';
+ }
+ }
+ $block = pad($block, 1) if $PRETTY;
+
+ return <<EOF;
+
+# CAPTURE
+\$stash->set($name, do {
+ my \$output = '';
+$block
+ \$output;
+});
+EOF
+
+}
+
+
+#------------------------------------------------------------------------
+# macro($name, $block, \@args)
+#------------------------------------------------------------------------
+
+sub macro {
+ my ($class, $ident, $block, $args) = @_;
+ $block = pad($block, 2) if $PRETTY;
+
+ if ($args) {
+ my $nargs = scalar @$args;
+ $args = join(', ', map { "'$_'" } @$args);
+ $args = $nargs > 1
+ ? "\@args{ $args } = splice(\@_, 0, $nargs)"
+ : "\$args{ $args } = shift";
+
+ return <<EOF;
+
+# MACRO
+\$stash->set('$ident', sub {
+ my \$output = '';
+ my (%args, \$params);
+ $args;
+ \$params = shift;
+ \$params = { } unless ref(\$params) eq 'HASH';
+ \$params = { \%args, %\$params };
+
+ my \$stash = \$context->localise(\$params);
+ eval {
+$block
+ };
+ \$stash = \$context->delocalise();
+ die \$@ if \$@;
+ return \$output;
+});
+EOF
+
+ }
+ else {
+ return <<EOF;
+
+# MACRO
+\$stash->set('$ident', sub {
+ my \$params = \$_[0] if ref(\$_[0]) eq 'HASH';
+ my \$output = '';
+
+ my \$stash = \$context->localise(\$params);
+ eval {
+$block
+ };
+ \$stash = \$context->delocalise();
+ die \$@ if \$@;
+ return \$output;
+});
+EOF
+ }
+}
+
+
+sub debug {
+ my ($class, $nameargs) = @_;
+ my ($file, $args) = @$nameargs;
+ my $hash = shift @$args;
+ $args = join(', ', @$file, @$args);
+ $args .= @$hash ? ', { ' . join(', ', @$hash) . ' }' : '';
+ return "$OUTPUT \$context->debugging($args); ## DEBUG ##";
+}
+
+
+1;
+
+__END__
+
diff --git a/lib/Template/Document.pm b/lib/Template/Document.pm
new file mode 100644
index 0000000..9e01548
--- /dev/null
+++ b/lib/Template/Document.pm
@@ -0,0 +1,482 @@
+##============================================================= -*-Perl-*-
+#
+# Template::Document
+#
+# DESCRIPTION
+# Module defining a class of objects which encapsulate compiled
+# templates, storing additional block definitions and metadata
+# as well as the compiled Perl sub-routine representing the main
+# template content.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Document.pm,v 2.65 2003/04/24 09:14:38 abw Exp $
+#
+#============================================================================
+
+package Template::Document;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $ERROR $COMPERR $DEBUG $AUTOLOAD );
+use base qw( Template::Base );
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.65 $ =~ /(\d+)\.(\d+)/);
+
+
+#========================================================================
+# ----- PUBLIC METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\%document)
+#
+# Creates a new self-contained Template::Document object which
+# encapsulates a compiled Perl sub-routine, $block, any additional
+# BLOCKs defined within the document ($defblocks, also Perl sub-routines)
+# and additional $metadata about the document.
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $doc) = @_;
+ my ($block, $defblocks, $metadata) = @$doc{ qw( BLOCK DEFBLOCKS METADATA ) };
+ $defblocks ||= { };
+ $metadata ||= { };
+
+ # evaluate Perl code in $block to create sub-routine reference if necessary
+ unless (ref $block) {
+ local $SIG{__WARN__} = \&catch_warnings;
+ $COMPERR = '';
+
+ # DON'T LOOK NOW! - blindly untainting can make you go blind!
+ $block =~ /(.*)/s;
+ $block = $1;
+
+ $block = eval $block;
+# $COMPERR .= "[$@]" if $@;
+# return $class->error($COMPERR)
+ return $class->error($@)
+ unless defined $block;
+ }
+
+ # same for any additional BLOCK definitions
+ @$defblocks{ keys %$defblocks } =
+ # MORE BLIND UNTAINTING - turn away if you're squeamish
+ map {
+ ref($_)
+ ? $_
+ : ( /(.*)/s && eval($1) or return $class->error($@) )
+ } values %$defblocks;
+
+ bless {
+ %$metadata,
+ _BLOCK => $block,
+ _DEFBLOCKS => $defblocks,
+ _HOT => 0,
+ }, $class;
+}
+
+
+#------------------------------------------------------------------------
+# block()
+#
+# Returns a reference to the internal sub-routine reference, _BLOCK,
+# that constitutes the main document template.
+#------------------------------------------------------------------------
+
+sub block {
+ return $_[0]->{ _BLOCK };
+}
+
+
+#------------------------------------------------------------------------
+# blocks()
+#
+# Returns a reference to a hash array containing any BLOCK definitions
+# from the template. The hash keys are the BLOCK nameand the values
+# are references to Template::Document objects. Returns 0 (# an empty hash)
+# if no blocks are defined.
+#------------------------------------------------------------------------
+
+sub blocks {
+ return $_[0]->{ _DEFBLOCKS };
+}
+
+
+#------------------------------------------------------------------------
+# process($context)
+#
+# Process the document in a particular context. Checks for recursion,
+# registers the document with the context via visit(), processes itself,
+# and then unwinds with a large gin and tonic.
+#------------------------------------------------------------------------
+
+sub process {
+ my ($self, $context) = @_;
+ my $defblocks = $self->{ _DEFBLOCKS };
+ my $output;
+
+
+ # check we're not already visiting this template
+ return $context->throw(Template::Constants::ERROR_FILE,
+ "recursion into '$self->{ name }'")
+ if $self->{ _HOT } && ! $context->{ RECURSION }; ## RETURN ##
+
+ $context->visit($defblocks);
+ $self->{ _HOT } = 1;
+ eval {
+ my $block = $self->{ _BLOCK };
+ $output = &$block($context);
+ };
+ $self->{ _HOT } = 0;
+ $context->leave();
+
+ die $context->catch($@)
+ if $@;
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# Provides pseudo-methods for read-only access to various internal
+# members.
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+# my ($pkg, $file, $line) = caller();
+# print STDERR "called $self->AUTOLOAD($method) from $file line $line\n";
+ return $self->{ $method };
+}
+
+
+#========================================================================
+# ----- PRIVATE METHODS -----
+#========================================================================
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal state
+# of the object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $dblks;
+ my $output = "$self : $self->{ name }\n";
+
+ $output .= "BLOCK: $self->{ _BLOCK }\nDEFBLOCKS:\n";
+
+ if ($dblks = $self->{ _DEFBLOCKS }) {
+ foreach my $b (keys %$dblks) {
+ $output .= " $b: $dblks->{ $b }\n";
+ }
+ }
+
+ return $output;
+}
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# as_perl($content)
+#
+# This method expects a reference to a hash passed as the first argument
+# containing 3 items:
+# METADATA # a hash of template metadata
+# BLOCK # string containing Perl sub definition for main block
+# DEFBLOCKS # hash containing further subs for addional BLOCK defs
+# It returns a string containing Perl code which, when evaluated and
+# executed, will instantiate a new Template::Document object with the
+# above data. On error, it returns undef with an appropriate error
+# message set in $ERROR.
+#------------------------------------------------------------------------
+
+sub as_perl {
+ my ($class, $content) = @_;
+ my ($block, $defblocks, $metadata) = @$content{ qw( BLOCK DEFBLOCKS METADATA ) };
+
+ $block =~ s/\n/\n /g;
+ $block =~ s/\s+$//;
+
+ $defblocks = join('', map {
+ my $code = $defblocks->{ $_ };
+ $code =~ s/\n/\n /g;
+ $code =~ s/\s*$//;
+ " '$_' => $code,\n";
+ } keys %$defblocks);
+ $defblocks =~ s/\s+$//;
+
+ $metadata = join('', map {
+ my $x = $metadata->{ $_ };
+ $x =~ s/(['\\])/\\$1/g;
+ " '$_' => '$x',\n";
+ } keys %$metadata);
+ $metadata =~ s/\s+$//;
+
+ return <<EOF
+#------------------------------------------------------------------------
+# Compiled template generated by the Template Toolkit version $Template::VERSION
+#------------------------------------------------------------------------
+
+$class->new({
+ METADATA => {
+$metadata
+ },
+ BLOCK => $block,
+ DEFBLOCKS => {
+$defblocks
+ },
+});
+EOF
+}
+
+
+#------------------------------------------------------------------------
+# write_perl_file($filename, \%content)
+#
+# This method calls as_perl() to generate the Perl code to represent a
+# compiled template with the content passed as the second argument.
+# It then writes this to the file denoted by the first argument.
+#
+# Returns 1 on success. On error, sets the $ERROR package variable
+# to contain an error message and returns undef.
+#------------------------------------------------------------------------
+
+sub write_perl_file {
+ my ($class, $file, $content) = @_;
+ my ($fh, $tmpfile);
+
+ return $class->error("invalid filename: $file")
+ unless $file =~ /^(.+)$/s;
+
+ eval {
+ require File::Temp;
+ require File::Basename;
+ ($fh, $tmpfile) = File::Temp::tempfile(
+ DIR => File::Basename::dirname($file)
+ );
+ print $fh $class->as_perl($content) || die $!;
+ close($fh);
+ };
+ return $class->error($@) if $@;
+ return rename($tmpfile, $file)
+ || $class->error($!);
+}
+
+
+#------------------------------------------------------------------------
+# catch_warnings($msg)
+#
+# Installed as
+#------------------------------------------------------------------------
+
+sub catch_warnings {
+ $COMPERR .= join('', @_);
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Document - Compiled template document object
+
+=head1 SYNOPSIS
+
+ use Template::Document;
+
+ $doc = Template::Document->new({
+ BLOCK => sub { # some perl code; return $some_text },
+ DEFBLOCKS => {
+ header => sub { # more perl code; return $some_text },
+ footer => sub { # blah blah blah; return $some_text },
+ },
+ METADATA => {
+ author => 'Andy Wardley',
+ version => 3.14,
+ }
+ }) || die $Template::Document::ERROR;
+
+ print $doc->process($context);
+
+=head1 DESCRIPTION
+
+This module defines an object class whose instances represent compiled
+template documents. The Template::Parser module creates a
+Template::Document instance to encapsulate a template as it is compiled
+into Perl code.
+
+The constructor method, new(), expects a reference to a hash array
+containing the BLOCK, DEFBLOCKS and METADATA items. The BLOCK item
+should contain a reference to a Perl subroutine or a textual
+representation of Perl code, as generated by the Template::Parser
+module, which is then evaluated into a subroutine reference using
+eval(). The DEFLOCKS item should reference a hash array containing
+further named BLOCKs which may be defined in the template. The keys
+represent BLOCK names and the values should be subroutine references
+or text strings of Perl code as per the main BLOCK item. The METADATA
+item should reference a hash array of metadata items relevant to the
+document.
+
+The process() method can then be called on the instantiated
+Template::Document object, passing a reference to a Template::Content
+object as the first parameter. This will install any locally defined
+blocks (DEFBLOCKS) in the the contexts() BLOCKS cache (via a call to
+visit()) so that they may be subsequently resolved by the context. The
+main BLOCK subroutine is then executed, passing the context reference
+on as a parameter. The text returned from the template subroutine is
+then returned by the process() method, after calling the context leave()
+method to permit cleanup and de-registration of named BLOCKS previously
+installed.
+
+An AUTOLOAD method provides access to the METADATA items for the document.
+The Template::Service module installs a reference to the main
+Template::Document object in the stash as the 'template' variable.
+This allows metadata items to be accessed from within templates,
+including PRE_PROCESS templates.
+
+header:
+
+ <html>
+ <head>
+ <title>[% template.title %]
+ </head>
+ ...
+
+Template::Document objects are usually created by the Template::Parser
+but can be manually instantiated or sub-classed to provide custom
+template components.
+
+=head1 METHODS
+
+=head2 new(\%config)
+
+Constructor method which accept a reference to a hash array containing the
+structure as shown in this example:
+
+ $doc = Template::Document->new({
+ BLOCK => sub { # some perl code; return $some_text },
+ DEFBLOCKS => {
+ header => sub { # more perl code; return $some_text },
+ footer => sub { # blah blah blah; return $some_text },
+ },
+ METADATA => {
+ author => 'Andy Wardley',
+ version => 3.14,
+ }
+ }) || die $Template::Document::ERROR;
+
+BLOCK and DEFBLOCKS items may be expressed as references to Perl subroutines
+or as text strings containing Perl subroutine definitions, as is generated
+by the Template::Parser module. These are evaluated into subroutine references
+using eval().
+
+Returns a new Template::Document object or undef on error. The error() class
+method can be called, or the $ERROR package variable inspected to retrieve
+the relevant error message.
+
+=head2 process($context)
+
+Main processing routine for the compiled template document. A reference to
+a Template::Context object should be passed as the first parameter. The
+method installs any locally defined blocks via a call to the context
+visit() method, processes it's own template, passing the context reference
+by parameter and then calls leave() in the context to allow cleanup.
+
+ print $doc->process($context);
+
+Returns a text string representing the generated output for the template.
+Errors are thrown via die().
+
+=head2 block()
+
+Returns a reference to the main BLOCK subroutine.
+
+=head2 blocks()
+
+Returns a reference to the hash array of named DEFBLOCKS subroutines.
+
+=head2 AUTOLOAD
+
+An autoload method returns METADATA items.
+
+ print $doc->author();
+
+=head1 PACKAGE SUB-ROUTINES
+
+=head2 write_perl_file(\%config)
+
+This package subroutine is provided to effect persistance of compiled
+templates. If the COMPILE_EXT option (to indicate a file extension
+for saving compiled templates) then the Template::Parser module calls
+this subroutine before calling the new() constructor. At this stage,
+the parser has a representation of the template as text strings
+containing Perl code. We can write that to a file, enclosed in a
+small wrapper which will allow us to susequently require() the file
+and have Perl parse and compile it into a Template::Document. Thus we
+have persistance of compiled templates.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.65, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Parser|Template::Parser>
diff --git a/lib/Template/Exception.pm b/lib/Template/Exception.pm
new file mode 100644
index 0000000..cf60cb3
--- /dev/null
+++ b/lib/Template/Exception.pm
@@ -0,0 +1,244 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Exception
+#
+# DESCRIPTION
+# Module implementing a generic exception class used for error handling
+# in the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------
+#
+# $Id: Exception.pm,v 2.59 2003/04/24 09:14:38 abw Exp $
+#
+#========================================================================
+
+
+package Template::Exception;
+
+require 5.005;
+
+use strict;
+use vars qw( $VERSION );
+
+use constant TYPE => 0;
+use constant INFO => 1;
+use constant TEXT => 2;
+use overload q|""| => "as_string", fallback => 1;
+
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.59 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# new($type, $info, \$text)
+#
+# Constructor method used to instantiate a new Template::Exception
+# object. The first parameter should contain the exception type. This
+# can be any arbitrary string of the caller's choice to represent a
+# specific exception. The second parameter should contain any
+# information (i.e. error message or data reference) relevant to the
+# specific exception event. The third optional parameter may be a
+# reference to a scalar containing output text from the template
+# block up to the point where the exception was thrown.
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $type, $info, $textref) = @_;
+ bless [ $type, $info, $textref ], $class;
+}
+
+
+#------------------------------------------------------------------------
+# type()
+# info()
+# type_info()
+#
+# Accessor methods to return the internal TYPE and INFO fields.
+#------------------------------------------------------------------------
+
+sub type {
+ $_[0]->[ TYPE ];
+}
+
+sub info {
+ $_[0]->[ INFO ];
+}
+
+sub type_info {
+ my $self = shift;
+ @$self[ TYPE, INFO ];
+}
+
+#------------------------------------------------------------------------
+# text()
+# text(\$pretext)
+#
+# Method to return the text referenced by the TEXT member. A text
+# reference may be passed as a parameter to supercede the existing
+# member. The existing text is added to the *end* of the new text
+# before being stored. This facility is provided for template blocks
+# to gracefully de-nest when an exception occurs and allows them to
+# reconstruct their output in the correct order.
+#------------------------------------------------------------------------
+
+sub text {
+ my ($self, $newtextref) = @_;
+ my $textref = $self->[ TEXT ];
+
+ if ($newtextref) {
+ $$newtextref .= $$textref if $textref && $textref ne $newtextref;
+ $self->[ TEXT ] = $newtextref;
+ return '';
+
+ }
+ elsif ($textref) {
+ return $$textref;
+ }
+ else {
+ return '';
+ }
+}
+
+
+#------------------------------------------------------------------------
+# as_string()
+#
+# Accessor method to return a string indicating the exception type and
+# information.
+#------------------------------------------------------------------------
+
+sub as_string {
+ my $self = shift;
+ return $self->[ TYPE ] . ' error - ' . $self->[ INFO ];
+}
+
+
+#------------------------------------------------------------------------
+# select_handler(@types)
+#
+# Selects the most appropriate handler for the exception TYPE, from
+# the list of types passed in as parameters. The method returns the
+# item which is an exact match for TYPE or the closest, more
+# generic handler (e.g. foo being more generic than foo.bar, etc.)
+#------------------------------------------------------------------------
+
+sub select_handler {
+ my ($self, @options) = @_;
+ my $type = $self->[ TYPE ];
+ my %hlut;
+ @hlut{ @options } = (1) x @options;
+
+ while ($type) {
+ return $type if $hlut{ $type };
+
+ # strip .element from the end of the exception type to find a
+ # more generic handler
+ $type =~ s/\.?[^\.]*$//;
+ }
+ return undef;
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Exception - Exception handling class module
+
+=head1 SYNOPSIS
+
+ use Template::Exception;
+
+ my $exception = Template::Exception->new($type, $info);
+ $type = $exception->type;
+ $info = $exception->info;
+ ($type, $info) = $exception->type_info;
+
+ print $exception->as_string();
+
+ $handler = $exception->select_handler(\@candidates);
+
+=head1 DESCRIPTION
+
+The Template::Exception module defines an object class for
+representing exceptions within the template processing life cycle.
+Exceptions can be raised by modules within the Template Toolkit, or
+can be generated and returned by user code bound to template
+variables.
+
+
+Exceptions can be raised in a template using the THROW directive,
+
+ [% THROW user.login 'no user id: please login' %]
+
+or by calling the throw() method on the current Template::Context object,
+
+ $context->throw('user.passwd', 'Incorrect Password');
+ $context->throw('Incorrect Password'); # type 'undef'
+
+or from Perl code by calling die() with a Template::Exception object,
+
+ die (Template::Exception->new('user.denied', 'Invalid User ID'));
+
+or by simply calling die() with an error string. This is
+automagically caught and converted to an exception of 'undef'
+type which can then be handled in the usual way.
+
+ die "I'm sorry Dave, I can't do that";
+
+
+
+Each exception is defined by its type and a information component
+(e.g. error message). The type can be any identifying string and may
+contain dotted components (e.g. 'foo', 'foo.bar', 'foo.bar.baz').
+Exception types are considered to be hierarchical such that 'foo.bar'
+would be a specific type of the more general 'foo' type.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.59, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Context|Template::Context>
diff --git a/lib/Template/Filters.pm b/lib/Template/Filters.pm
new file mode 100644
index 0000000..8667c6a
--- /dev/null
+++ b/lib/Template/Filters.pm
@@ -0,0 +1,1438 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Filters
+#
+# DESCRIPTION
+# Defines filter plugins as used by the FILTER directive.
+#
+# AUTHORS
+# Andy Wardley <abw@kfs.org>, with a number of filters contributed
+# by Leslie Michael Orchard <deus_x@nijacode.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Filters.pm,v 2.72 2003/07/01 12:43:55 darren Exp $
+#
+#============================================================================
+
+package Template::Filters;
+
+require 5.004;
+
+use strict;
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG $FILTERS $URI_ESCAPES $PLUGIN_FILTER );
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.72 $ =~ /(\d+)\.(\d+)/);
+
+
+#------------------------------------------------------------------------
+# standard filters, defined in one of the following forms:
+# name => \&static_filter
+# name => [ \&subref, $is_dynamic ]
+# If the $is_dynamic flag is set then the sub-routine reference
+# is called to create a new filter each time it is requested; if
+# not set, then it is a single, static sub-routine which is returned
+# for every filter request for that name.
+#------------------------------------------------------------------------
+
+$FILTERS = {
+ # static filters
+ 'html' => \&html_filter,
+ 'html_para' => \&html_paragraph,
+ 'html_break' => \&html_para_break,
+ 'html_para_break' => \&html_para_break,
+ 'html_line_break' => \&html_line_break,
+ 'uri' => \&uri_filter,
+ 'upper' => sub { uc $_[0] },
+ 'lower' => sub { lc $_[0] },
+ 'ucfirst' => sub { ucfirst $_[0] },
+ 'lcfirst' => sub { lcfirst $_[0] },
+ 'stderr' => sub { print STDERR @_; return '' },
+ 'trim' => sub { for ($_[0]) { s/^\s+//; s/\s+$// }; $_[0] },
+ 'null' => sub { return '' },
+ 'collapse' => sub { for ($_[0]) { s/^\s+//; s/\s+$//; s/\s+/ /g };
+ $_[0] },
+
+ # dynamic filters
+ 'html_entity' => [ \&html_entity_filter_factory, 1 ],
+ 'indent' => [ \&indent_filter_factory, 1 ],
+ 'format' => [ \&format_filter_factory, 1 ],
+ 'truncate' => [ \&truncate_filter_factory, 1 ],
+ 'repeat' => [ \&repeat_filter_factory, 1 ],
+ 'replace' => [ \&replace_filter_factory, 1 ],
+ 'remove' => [ \&remove_filter_factory, 1 ],
+ 'eval' => [ \&eval_filter_factory, 1 ],
+ 'evaltt' => [ \&eval_filter_factory, 1 ], # alias
+ 'perl' => [ \&perl_filter_factory, 1 ],
+ 'evalperl' => [ \&perl_filter_factory, 1 ], # alias
+ 'redirect' => [ \&redirect_filter_factory, 1 ],
+ 'file' => [ \&redirect_filter_factory, 1 ], # alias
+ 'stdout' => [ \&stdout_filter_factory, 1 ],
+ 'latex' => [ \&latex_filter_factory, 1 ],
+};
+
+# name of module implementing plugin filters
+$PLUGIN_FILTER = 'Template::Plugin::Filter';
+
+
+#========================================================================
+# -- PUBLIC METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# fetch($name, \@args, $context)
+#
+# Attempts to instantiate or return a reference to a filter sub-routine
+# named by the first parameter, $name, with additional constructor
+# arguments passed by reference to a list as the second parameter,
+# $args. A reference to the calling Template::Context object is
+# passed as the third paramter.
+#
+# Returns a reference to a filter sub-routine or a pair of values
+# (undef, STATUS_DECLINED) or ($error, STATUS_ERROR) to decline to
+# deliver the filter or to indicate an error.
+#------------------------------------------------------------------------
+
+sub fetch {
+ my ($self, $name, $args, $context) = @_;
+ my ($factory, $is_dynamic, $filter, $error);
+
+ $self->debug("fetch($name, ",
+ defined $args ? ('[ ', join(', ', @$args), ' ]') : '<no args>', ', ',
+ defined $context ? $context : '<no context>',
+ ')') if $self->{ DEBUG };
+
+ # allow $name to be specified as a reference to
+ # a plugin filter object; any other ref is
+ # assumed to be a coderef and hence already a filter;
+ # non-refs are assumed to be regular name lookups
+
+ if (ref $name) {
+ if (UNIVERSAL::isa($name, $PLUGIN_FILTER)) {
+ $factory = $name->factory()
+ || return $self->error($name->error());
+ }
+ else {
+ return $name;
+ }
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DECLINED)
+ unless ($factory = $self->{ FILTERS }->{ $name }
+ || $FILTERS->{ $name });
+ }
+
+ # factory can be an [ $code, $dynamic ] or just $code
+ if (ref $factory eq 'ARRAY') {
+ ($factory, $is_dynamic) = @$factory;
+ }
+ else {
+ $is_dynamic = 0;
+ }
+
+ if (ref $factory eq 'CODE') {
+ if ($is_dynamic) {
+ # if the dynamic flag is set then the sub-routine is a
+ # factory which should be called to create the actual
+ # filter...
+ eval {
+ ($filter, $error) = &$factory($context, $args ? @$args : ());
+ };
+ $error ||= $@;
+ $error = "invalid FILTER for '$name' (not a CODE ref)"
+ unless $error || ref($filter) eq 'CODE';
+ }
+ else {
+ # ...otherwise, it's a static filter sub-routine
+ $filter = $factory;
+ }
+ }
+ else {
+ $error = "invalid FILTER entry for '$name' (not a CODE ref)";
+ }
+
+ if ($error) {
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR) ;
+ }
+ else {
+ return $filter;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# store($name, \&filter)
+#
+# Stores a new filter in the internal FILTERS hash. The first parameter
+# is the filter name, the second a reference to a subroutine or
+# array, as per the standard $FILTERS entries.
+#------------------------------------------------------------------------
+
+sub store {
+ my ($self, $name, $filter) = @_;
+
+ $self->debug("store($name, $filter)") if $self->{ DEBUG };
+
+ $self->{ FILTERS }->{ $name } = $filter;
+ return 1;
+}
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# _init(\%config)
+#
+# Private initialisation method.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $params) = @_;
+
+ $self->{ FILTERS } = $params->{ FILTERS } || { };
+ $self->{ TOLERANT } = $params->{ TOLERANT } || 0;
+ $self->{ DEBUG } = ( $params->{ DEBUG } || 0 )
+ & Template::Constants::DEBUG_FILTERS;
+
+
+ return $self;
+}
+
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $output = "[Template::Filters] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ foreach $key (qw( TOLERANT )) {
+ my $val = $self->{ $key };
+ $val = '<undef>' unless defined $val;
+ $output .= sprintf($format, $key, $val);
+ }
+
+ my $filters = $self->{ FILTERS };
+ $filters = join('', map {
+ sprintf(" $format", $_, $filters->{ $_ });
+ } keys %$filters);
+ $filters = "{\n$filters }";
+
+ $output .= sprintf($format, 'FILTERS (local)' => $filters);
+
+ $filters = $FILTERS;
+ $filters = join('', map {
+ my $f = $filters->{ $_ };
+ my ($ref, $dynamic) = ref $f eq 'ARRAY' ? @$f : ($f, 0);
+ sprintf(" $format", $_, $dynamic ? 'dynamic' : 'static');
+ } sort keys %$filters);
+ $filters = "{\n$filters }";
+
+ $output .= sprintf($format, 'FILTERS (global)' => $filters);
+
+ $output .= '}';
+ return $output;
+}
+
+
+#========================================================================
+# -- STATIC FILTER SUBS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# uri_filter() [% FILTER uri %]
+#
+# URI escape a string. This code is borrowed from Gisle Aas' URI::Escape
+# module. For something so simple, I can't see any validation in making
+# the user install the URI modules just for this, so we cut and paste.
+#
+# URI::Escape is Copyright 1995-2000 Gisle Aas.
+#------------------------------------------------------------------------
+
+sub uri_filter {
+ my $text = shift;
+
+ # construct and cache a lookup table for escapes (faster than
+ # doing a sprintf() for every character in every string each
+ # time)
+ $URI_ESCAPES ||= {
+ map { ( chr($_), sprintf("%%%02X", $_) ) } (0..255),
+ };
+
+ $text =~ s/([^;\/?:@&=+\$,A-Za-z0-9\-_.!~*'()])/$URI_ESCAPES->{$1}/g;
+ $text;
+}
+
+
+#------------------------------------------------------------------------
+# html_filter() [% FILTER html %]
+#
+# Convert any '<', '>' or '&' characters to the HTML equivalents, '&lt;',
+# '&gt;' and '&amp;', respectively.
+#------------------------------------------------------------------------
+
+sub html_filter {
+ my $text = shift;
+ for ($text) {
+ s/&/&amp;/g;
+ s/</&lt;/g;
+ s/>/&gt;/g;
+ s/"/&quot;/g;
+ }
+ return $text;
+}
+
+
+#------------------------------------------------------------------------
+# html_paragraph() [% FILTER html_para %]
+#
+# Wrap each paragraph of text (delimited by two or more newlines) in the
+# <p>...</p> HTML tags.
+#------------------------------------------------------------------------
+
+sub html_paragraph {
+ my $text = shift;
+ return "<p>\n"
+ . join("\n</p>\n\n<p>\n", split(/(?:\r?\n){2,}/, $text))
+ . "</p>\n";
+}
+
+
+#------------------------------------------------------------------------
+# html_para_break() [% FILTER html_para_break %]
+#
+# Join each paragraph of text (delimited by two or more newlines) with
+# <br><br> HTML tags.
+#------------------------------------------------------------------------
+
+sub html_para_break {
+ my $text = shift;
+ $text =~ s|(\r?\n){2,}|$1<br />$1<br />$1|g;
+ return $text;
+}
+
+#------------------------------------------------------------------------
+# html_line_break() [% FILTER html_line_break %]
+#
+# replaces any newlines with <br> HTML tags.
+#------------------------------------------------------------------------
+
+sub html_line_break {
+ my $text = shift;
+ $text =~ s|(\r?\n)|<br />$1|g;
+ return $text;
+}
+
+#========================================================================
+# -- DYNAMIC FILTER FACTORIES --
+#========================================================================
+
+#------------------------------------------------------------------------
+# html_entity_filter_factory(\%options) [% FILTER html %]
+#
+# Dynamic version of the static html filter which attempts to locate the
+# Apache::Util or HTML::Entities modules to perform full entity encoding
+# of the text passed. Returns an exception if one or other of the
+# modules can't be located.
+#------------------------------------------------------------------------
+
+sub html_entity_filter_factory {
+ my $context = shift;
+
+ # if Apache::Util is installed then we use it
+ eval {
+ require Apache::Util;
+ Apache::Util::escape_html('');
+ };
+ return \&Apache::Util::escape_html
+ unless $@;
+
+ # otherwise if HTML::Entities is installed then we use that
+ eval {
+ require HTML::Entities;
+ };
+ return \&HTML::Entities::encode_entities
+ unless $@;
+
+ return (undef, Template::Exception->new( html_entity =>
+ 'cannot locate Apache::Util or HTML::Entities' ));
+
+}
+
+
+#------------------------------------------------------------------------
+# indent_filter_factory($pad) [% FILTER indent(pad) %]
+#
+# Create a filter to indent text by a fixed pad string or when $pad is
+# numerical, a number of space.
+#------------------------------------------------------------------------
+
+sub indent_filter_factory {
+ my ($context, $pad) = @_;
+ $pad = 4 unless defined $pad;
+ $pad = ' ' x $pad if $pad =~ /^\d+$/;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ $text =~ s/^/$pad/mg;
+ return $text;
+ }
+}
+
+#------------------------------------------------------------------------
+# format_filter_factory() [% FILTER format(format) %]
+#
+# Create a filter to format text according to a printf()-like format
+# string.
+#------------------------------------------------------------------------
+
+sub format_filter_factory {
+ my ($context, $format) = @_;
+ $format = '%s' unless defined $format;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ return join("\n", map{ sprintf($format, $_) } split(/\n/, $text));
+ }
+}
+
+
+#------------------------------------------------------------------------
+# repeat_filter_factory($n) [% FILTER repeat(n) %]
+#
+# Create a filter to repeat text n times.
+#------------------------------------------------------------------------
+
+sub repeat_filter_factory {
+ my ($context, $iter) = @_;
+ $iter = 1 unless defined $iter and length $iter;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ return join('\n', $text) x $iter;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# replace_filter_factory($s, $r) [% FILTER replace(search, replace) %]
+#
+# Create a filter to replace 'search' text with 'replace'
+#------------------------------------------------------------------------
+
+sub replace_filter_factory {
+ my ($context, $search, $replace) = @_;
+ $search = '' unless defined $search;
+ $replace = '' unless defined $replace;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ $text =~ s/$search/$replace/g;
+ return $text;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# remove_filter_factory($text) [% FILTER remove(text) %]
+#
+# Create a filter to remove 'search' string from the input text.
+#------------------------------------------------------------------------
+
+sub remove_filter_factory {
+ my ($context, $search) = @_;
+
+ return sub {
+ my $text = shift;
+ $text = '' unless defined $text;
+ $text =~ s/$search//g;
+ return $text;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# truncate_filter_factory($n) [% FILTER truncate(n) %]
+#
+# Create a filter to truncate text after n characters.
+#------------------------------------------------------------------------
+
+sub truncate_filter_factory {
+ my ($context, $len) = @_;
+ $len = 32 unless defined $len;
+
+ return sub {
+ my $text = shift;
+ return $text if length $text < $len;
+ return substr($text, 0, $len - 3) . "...";
+ }
+}
+
+
+#------------------------------------------------------------------------
+# eval_filter_factory [% FILTER eval %]
+#
+# Create a filter to evaluate template text.
+#------------------------------------------------------------------------
+
+sub eval_filter_factory {
+ my $context = shift;
+
+ return sub {
+ my $text = shift;
+ $context->process(\$text);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# perl_filter_factory [% FILTER perl %]
+#
+# Create a filter to process Perl text iff the context EVAL_PERL flag
+# is set.
+#------------------------------------------------------------------------
+
+sub perl_filter_factory {
+ my $context = shift;
+ my $stash = $context->stash;
+
+ return (undef, Template::Exception->new('perl', 'EVAL_PERL is not set'))
+ unless $context->eval_perl();
+
+ return sub {
+ my $text = shift;
+ local($Template::Perl::context) = $context;
+ local($Template::Perl::stash) = $stash;
+ my $out = eval <<EOF;
+package Template::Perl;
+\$stash = \$context->stash();
+$text
+EOF
+ $context->throw($@) if $@;
+ return $out;
+ }
+}
+
+
+#------------------------------------------------------------------------
+# redirect_filter_factory($context, $file) [% FILTER redirect(file) %]
+#
+# Create a filter to redirect the block text to a file.
+#------------------------------------------------------------------------
+
+sub redirect_filter_factory {
+ my ($context, $file, $options) = @_;
+ my $outpath = $context->config->{ OUTPUT_PATH };
+
+ return (undef, Template::Exception->new('redirect',
+ 'OUTPUT_PATH is not set'))
+ unless $outpath;
+
+ $options = { binmode => $options } unless ref $options;
+
+ sub {
+ my $text = shift;
+ my $outpath = $context->config->{ OUTPUT_PATH }
+ || return '';
+ $outpath .= "/$file";
+ my $error = Template::_output($outpath, \$text, $options);
+ die Template::Exception->new('redirect', $error)
+ if $error;
+ return '';
+ }
+}
+
+
+#------------------------------------------------------------------------
+# stdout_filter_factory($context, $binmode) [% FILTER stdout(binmode) %]
+#
+# Create a filter to print a block to stdout, with an optional binmode.
+#------------------------------------------------------------------------
+
+sub stdout_filter_factory {
+ my ($context, $options) = @_;
+
+ $options = { binmode => $options } unless ref $options;
+
+ sub {
+ my $text = shift;
+ binmode(STDOUT) if $options->{ binmode };
+ print STDOUT $text;
+ return '';
+ }
+}
+
+
+#------------------------------------------------------------------------
+# latex_filter_factory($context, $outputType) [% FILTER latex(outputType) %]
+#
+# Return a filter sub that converts a (hopefully) complete LaTeX source
+# file to either "ps", "dvi", or "pdf". Output type should be "ps", "dvi"
+# or "pdf" (pdf is default).
+#
+# Creates a temporary directory below File::Spec->tmpdir() (often /tmp)
+# and writes the text into doc.tex. It then runs either pdflatex or
+# latex and optionally dvips. Based on the exit status either returns
+# the entire doc.(pdf|ps|dvi) output or throws an error with a summary
+# of the error messages from doc.log.
+#
+# Written by Craig Barratt, Apr 28 2001.
+# Win32 additions by Richard Tietjen.
+#------------------------------------------------------------------------
+use File::Path;
+use File::Spec;
+use Cwd;
+
+sub latex_filter_factory
+{
+ my($context, $output) = @_;
+
+ $output = lc($output);
+ my $fName = "latex";
+ my($LaTeXPath, $PdfLaTeXPath, $DviPSPath)
+ = @{Template::Config->latexpaths()};
+ if ( $output eq "ps" || $output eq "dvi" ) {
+ $context->throw($fName,
+ "latex not installed (see Template::Config::LATEX_PATH)")
+ if ( $LaTeXPath eq "" );
+ } else {
+ $output = "pdf";
+ $LaTeXPath = $PdfLaTeXPath;
+ $context->throw($fName,
+ "pdflatex not installed (see Template::Config::PDFLATEX_PATH)")
+ if ( $LaTeXPath eq "" );
+ }
+ if ( $output eq "ps" && $DviPSPath eq "" ) {
+ $context->throw($fName,
+ "dvips not installed (see Template::Config::DVIPS_PATH)");
+ }
+ if ( $^O !~ /^(MacOS|os2|VMS)$/i ) {
+ return sub {
+ local(*FH);
+ my $text = shift;
+ my $tmpRootDir = File::Spec->tmpdir();
+ my $cnt = 0;
+ my($tmpDir, $fileName, $devnull);
+ my $texDoc = 'doc';
+
+ do {
+ $tmpDir = File::Spec->catdir($tmpRootDir,
+ "tt2latex$$" . "_$cnt");
+ $cnt++;
+ } while ( -e $tmpDir );
+ mkpath($tmpDir, 0, 0700);
+ $context->throw($fName, "can't create temp dir $tmpDir")
+ if ( !-d $tmpDir );
+ $fileName = File::Spec->catfile($tmpDir, "$texDoc.tex");
+ $devnull = File::Spec->devnull();
+ if ( !open(FH, ">$fileName") ) {
+ rmtree($tmpDir);
+ $context->throw($fName, "can't open $fileName for output");
+ }
+ print(FH $text);
+ close(FH);
+
+ # latex must run in tmpDir directory
+ my $currDir = cwd();
+ if ( !chdir($tmpDir) ) {
+ rmtree($tmpDir);
+ $context->throw($fName, "can't chdir $tmpDir");
+ }
+ #
+ # We don't need to quote the backslashes on windows, but we
+ # do on other OSs
+ #
+ my $LaTeX_arg = "\\nonstopmode\\input{$texDoc}";
+ $LaTeX_arg = "'$LaTeX_arg'" if ( $^O ne 'MSWin32' );
+ if ( system("$LaTeXPath $LaTeX_arg"
+ . " 1>$devnull 2>$devnull 0<$devnull") ) {
+ my $texErrs = "";
+ $fileName = File::Spec->catfile($tmpDir, "$texDoc.log");
+ if ( open(FH, "<$fileName") ) {
+ my $state = 0;
+ #
+ # Try to extract just the interesting errors from
+ # the verbose log file
+ #
+ while ( <FH> ) {
+ #
+ # TeX errors seems to start with a "!" at the
+ # start of the line, and are followed several
+ # lines later by a line designator of the
+ # form "l.nnn" where nnn is the line number.
+ # We make sure we pick up every /^!/ line, and
+ # the first /^l.\d/ line after each /^!/ line.
+ #
+ if ( /^(!.*)/ ) {
+ $texErrs .= $1 . "\n";
+ $state = 1;
+ }
+ if ( $state == 1 && /^(l\.\d.*)/ ) {
+ $texErrs .= $1 . "\n";
+ $state = 0;
+ }
+ }
+ close(FH);
+ } else {
+ $texErrs = "Unable to open $fileName\n";
+ }
+ my $ok = chdir($currDir);
+ rmtree($tmpDir);
+ $context->throw($fName, "can't chdir $currDir") if ( !$ok );
+ $context->throw($fName, "latex exited with errors:\n$texErrs");
+ }
+ if ( $output eq "ps" ) {
+ $fileName = File::Spec->catfile($tmpDir, "$texDoc.dvi");
+ if ( system("$DviPSPath $texDoc -o"
+ . " 1>$devnull 2>$devnull 0<$devnull") ) {
+ my $ok = chdir($currDir);
+ rmtree($tmpDir);
+ $context->throw($fName, "can't chdir $currDir") if ( !$ok );
+ $context->throw($fName, "can't run $DviPSPath $fileName");
+ }
+ }
+ if ( !chdir($currDir) ) {
+ rmtree($tmpDir);
+ $context->throw($fName, "can't chdir $currDir");
+ }
+
+ my $retStr;
+ $fileName = File::Spec->catfile($tmpDir, "$texDoc.$output");
+ if ( open(FH, $fileName) ) {
+ local $/ = undef; # slurp file in one go
+ binmode(FH);
+ $retStr = <FH>;
+ close(FH);
+ } else {
+ rmtree($tmpDir);
+ $context->throw($fName, "Can't open output file $fileName");
+ }
+ rmtree($tmpDir);
+ return $retStr;
+ }
+ } else {
+ $context->throw("$fName not yet supported on $^O OS."
+ . " Please contribute code!!");
+ }
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Filters - Post-processing filters for template blocks
+
+=head1 SYNOPSIS
+
+ use Template::Filters;
+
+ $filters = Template::Filters->new(\%config);
+
+ ($filter, $error) = $filters->fetch($name, \@args, $context);
+
+=head1 DESCRIPTION
+
+The Template::Filters module implements a provider for creating and/or
+returning subroutines that implement the standard filters. Additional
+custom filters may be provided via the FILTERS options.
+
+=head1 METHODS
+
+=head2 new(\%params)
+
+Constructor method which instantiates and returns a reference to a
+Template::Filters object. A reference to a hash array of configuration
+items may be passed as a parameter. These are described below.
+
+ my $filters = Template::Filters->new({
+ FILTERS => { ... },
+ });
+
+ my $template = Template->new({
+ LOAD_FILTERS => [ $filters ],
+ });
+
+A default Template::Filters module is created by the Template.pm module
+if the LOAD_FILTERS option isn't specified. All configuration parameters
+are forwarded to the constructor.
+
+ $template = Template->new({
+ FILTERS => { ... },
+ });
+
+=head2 fetch($name, \@args, $context)
+
+Called to request that a filter of a given name be provided. The name
+of the filter should be specified as the first parameter. This should
+be one of the standard filters or one specified in the FILTERS
+configuration hash. The second argument should be a reference to an
+array containing configuration parameters for the filter. This may be
+specified as 0, or undef where no parameters are provided. The third
+argument should be a reference to the current Template::Context
+object.
+
+The method returns a reference to a filter sub-routine on success. It
+may also return (undef, STATUS_DECLINE) to decline the request, to allow
+delegation onto other filter providers in the LOAD_FILTERS chain of
+responsibility. On error, ($error, STATUS_ERROR) is returned where $error
+is an error message or Template::Exception object indicating the error
+that occurred.
+
+When the TOLERANT option is set, errors are automatically downgraded to
+a STATUS_DECLINE response.
+
+
+=head1 CONFIGURATION OPTIONS
+
+The following list details the configuration options that can be provided
+to the Template::Filters new() constructor.
+
+=over 4
+
+
+
+
+=item FILTERS
+
+The FILTERS option can be used to specify custom filters which can
+then be used with the FILTER directive like any other. These are
+added to the standard filters which are available by default. Filters
+specified via this option will mask any standard filters of the same
+name.
+
+The FILTERS option should be specified as a reference to a hash array
+in which each key represents the name of a filter. The corresponding
+value should contain a reference to an array containing a subroutine
+reference and a flag which indicates if the filter is static (0) or
+dynamic (1). A filter may also be specified as a solitary subroutine
+reference and is assumed to be static.
+
+ $filters = Template::Filters->new({
+ FILTERS => {
+ 'sfilt1' => \&static_filter, # static
+ 'sfilt2' => [ \&static_filter, 0 ], # same as above
+ 'dfilt1' => [ \&dyanamic_filter_factory, 1 ],
+ },
+ });
+
+Additional filters can be specified at any time by calling the
+define_filter() method on the current Template::Context object.
+The method accepts a filter name, a reference to a filter
+subroutine and an optional flag to indicate if the filter is
+dynamic.
+
+ my $context = $template->context();
+ $context->define_filter('new_html', \&new_html);
+ $context->define_filter('new_repeat', \&new_repeat, 1);
+
+Static filters are those where a single subroutine reference is used
+for all invocations of a particular filter. Filters that don't accept
+any configuration parameters (e.g. 'html') can be implemented
+statically. The subroutine reference is simply returned when that
+particular filter is requested. The subroutine is called to filter
+the output of a template block which is passed as the only argument.
+The subroutine should return the modified text.
+
+ sub static_filter {
+ my $text = shift;
+ # do something to modify $text...
+ return $text;
+ }
+
+The following template fragment:
+
+ [% FILTER sfilt1 %]
+ Blah blah blah.
+ [% END %]
+
+is approximately equivalent to:
+
+ &static_filter("\nBlah blah blah.\n");
+
+Filters that can accept parameters (e.g. 'truncate') should be
+implemented dynamically. In this case, the subroutine is taken to be
+a filter 'factory' that is called to create a unique filter subroutine
+each time one is requested. A reference to the current
+Template::Context object is passed as the first parameter, followed by
+any additional parameters specified. The subroutine should return
+another subroutine reference (usually a closure) which implements the
+filter.
+
+ sub dynamic_filter_factory {
+ my ($context, @args) = @_;
+
+ return sub {
+ my $text = shift;
+ # do something to modify $text...
+ return $text;
+ }
+ }
+
+The following template fragment:
+
+ [% FILTER dfilt1(123, 456) %]
+ Blah blah blah
+ [% END %]
+
+is approximately equivalent to:
+
+ my $filter = &dynamic_filter_factory($context, 123, 456);
+ &$filter("\nBlah blah blah.\n");
+
+See the FILTER directive for further examples.
+
+
+
+
+=item TOLERANT
+
+The TOLERANT flag is used by the various Template Toolkit provider
+modules (Template::Provider, Template::Plugins, Template::Filters) to
+control their behaviour when errors are encountered. By default, any
+errors are reported as such, with the request for the particular
+resource (template, plugin, filter) being denied and an exception
+raised. When the TOLERANT flag is set to any true values, errors will
+be silently ignored and the provider will instead return
+STATUS_DECLINED. This allows a subsequent provider to take
+responsibility for providing the resource, rather than failing the
+request outright. If all providers decline to service the request,
+either through tolerated failure or a genuine disinclination to
+comply, then a 'E<lt>resourceE<gt> not found' exception is raised.
+
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging messages from the
+Template::Filters module by setting it to include the DEBUG_FILTERS
+value.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_FILTERS | DEBUG_PLUGINS,
+ });
+
+
+
+
+=back
+
+=head1 TEMPLATE TOOLKIT FILTERS
+
+The following standard filters are distributed with the Template Toolkit.
+
+
+
+=head2 format(format)
+
+The 'format' filter takes a format string as a parameter (as per
+printf()) and formats each line of text accordingly.
+
+ [% FILTER format('<!-- %-40s -->') %]
+ This is a block of text filtered
+ through the above format.
+ [% END %]
+
+output:
+
+ <!-- This is a block of text filtered -->
+ <!-- through the above format. -->
+
+=head2 upper
+
+Folds the input to UPPER CASE.
+
+ [% "hello world" FILTER upper %]
+
+output:
+
+ HELLO WORLD
+
+=head2 lower
+
+Folds the input to lower case.
+
+ [% "Hello World" FILTER lower %]
+
+output:
+
+ hello world
+
+=head2 ucfirst
+
+Folds the first character of the input to UPPER CASE.
+
+ [% "hello" FILTER ucfirst %]
+
+output:
+
+ Hello
+
+=head2 lcfirst
+
+Folds the first character of the input to lower case.
+
+ [% "HELLO" FILTER lcfirst %]
+
+output:
+
+ hELLO
+
+=head2 trim
+
+Trims any leading or trailing whitespace from the input text. Particularly
+useful in conjunction with INCLUDE, PROCESS, etc., having the same effect
+as the TRIM configuration option.
+
+ [% INCLUDE myfile | trim %]
+
+=head2 collapse
+
+Collapse any whitespace sequences in the input text into a single space.
+Leading and trailing whitespace (which would be reduced to a single space)
+is removed, as per trim.
+
+ [% FILTER collapse %]
+
+ The cat
+
+ sat on
+
+ the mat
+
+ [% END %]
+
+output:
+
+ The cat sat on the mat
+
+=head2 html
+
+Converts the characters 'E<lt>', 'E<gt>' and '&' to '&lt;', '&gt;' and
+'&amp;', respectively, protecting them from being interpreted as
+representing HTML tags or entities.
+
+ [% FILTER html %]
+ Binary "<=>" returns -1, 0, or 1 depending on...
+ [% END %]
+
+output:
+
+ Binary "&lt;=&gt;" returns -1, 0, or 1 depending on...
+
+=head2 html_entity
+
+The html filter is fast and simple but it doesn't encode the full
+range of HTML entities that your text may contain. The html_entity
+filter uses either the Apache::Util module (which is written in C and
+is therefore faster) or the HTML::Entities module (written in Perl but
+equally as comprehensive) to perform the encoding. If one or other of
+these modules are installed on your system then the text will be
+encoded (via the escape_html() or encode_entities() subroutines
+respectively) to convert all extended characters into their
+appropriate HTML entities (e.g. converting 'é' to '&eacute;'). If
+neither module is available on your system then an 'html_entity' exception
+will be thrown reporting an appropriate message.
+
+For further information on HTML entity encoding, see
+http://www.w3.org/TR/REC-html40/sgml/entities.html.
+
+=head2 html_para
+
+This filter formats a block of text into HTML paragraphs. A sequence of
+two or more newlines is used as the delimiter for paragraphs which are
+then wrapped in HTML E<lt>pE<gt>...E<lt>/pE<gt> tags.
+
+ [% FILTER html_para %]
+ The cat sat on the mat.
+
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ <p>
+ The cat sat on the mat.
+ </p>
+
+ <p>
+ Mary had a little lamb.
+ </p>
+
+=head2 html_break / html_para_break
+
+Similar to the html_para filter described above, but uses the HTML tag
+sequence E<lt>brE<gt>E<lt>brE<gt> to join paragraphs.
+
+ [% FILTER html_break %]
+ The cat sat on the mat.
+
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ The cat sat on the mat.
+ <br>
+ <br>
+ Mary had a little lamb.
+
+=head2 html_line_break
+
+This filter replaces any newlines with E<lt>brE<gt> HTML tags,
+thus preserving the line breaks of the original text in the
+HTML output.
+
+ [% FILTER html_line_break %]
+ The cat sat on the mat.
+ Mary had a little lamb.
+ [% END %]
+
+output:
+
+ The cat sat on the mat.<br>
+ Mary had a little lamb.<br>
+
+=head2 uri
+
+This filter URI escapes the input text, converting any characters
+outside of the permitted URI character set (as defined by RFC 2396)
+into a C<%nn> hex escape.
+
+ [% 'my file.html' | uri %]
+
+output:
+
+ my%20file.html
+
+Note that URI escaping isn't always enough when generating hyperlinks in
+an HTML document. The C<&> character, for example, is valid in a URI and
+will not be escaped by the URI filter. In this case you should also filter
+the text through the 'html' filter.
+
+ <a href="[% filename | uri | html %]">click here</a>
+
+=head2 indent(pad)
+
+Indents the text block by a fixed pad string or width. The 'pad' argument
+can be specified as a string, or as a numerical value to indicate a pad
+width (spaces). Defaults to 4 spaces if unspecified.
+
+ [% FILTER indent('ME> ') %]
+ blah blah blah
+ cabbages, rhubard, onions
+ [% END %]
+
+output:
+
+ ME> blah blah blah
+ ME> cabbages, rhubard, onions
+
+=head2 truncate(length)
+
+Truncates the text block to the length specified, or a default length of
+32. Truncated text will be terminated with '...' (i.e. the '...' falls
+inside the required length, rather than appending to it).
+
+ [% FILTER truncate(21) %]
+ I have much to say on this matter that has previously
+ been said on more than one occasion.
+ [% END %]
+
+output:
+
+ I have much to say...
+
+=head2 repeat(iterations)
+
+Repeats the text block for as many iterations as are specified (default: 1).
+
+ [% FILTER repeat(3) %]
+ We want more beer and we want more beer,
+ [% END %]
+ We are the more beer wanters!
+
+output:
+
+ We want more beer and we want more beer,
+ We want more beer and we want more beer,
+ We want more beer and we want more beer,
+ We are the more beer wanters!
+
+=head2 remove(string)
+
+Searches the input text for any occurrences of the specified string and
+removes them. A Perl regular expression may be specified as the search
+string.
+
+ [% "The cat sat on the mat" FILTER remove('\s+') %]
+
+output:
+
+ Thecatsatonthemat
+
+=head2 replace(search, replace)
+
+Similar to the remove filter described above, but taking a second parameter
+which is used as a replacement string for instances of the search string.
+
+ [% "The cat sat on the mat" | replace('\s+', '_') %]
+
+output:
+
+ The_cat_sat_on_the_mat
+
+=head2 redirect(file, options)
+
+The 'redirect' filter redirects the output of the block into a separate
+file, specified relative to the OUTPUT_PATH configuration item.
+
+ [% FOREACH user = myorg.userlist %]
+ [% FILTER redirect("users/${user.id}.html") %]
+ [% INCLUDE userinfo %]
+ [% END %]
+ [% END %]
+
+or more succinctly, using side-effect notation:
+
+ [% INCLUDE userinfo
+ FILTER redirect("users/${user.id}.html")
+ FOREACH user = myorg.userlist
+ %]
+
+A 'file' exception will be thrown if the OUTPUT_PATH option is undefined.
+
+An optional 'binmode' argument can follow the filename to explicitly set
+the output file to binary mode.
+
+ [% PROCESS my/png/generator
+ FILTER redirect("images/logo.png", binmode=1) %]
+
+For backwards compatibility with earlier versions, a single true/false
+value can be used to set binary mode.
+
+ [% PROCESS my/png/generator
+ FILTER redirect("images/logo.png", 1) %]
+
+For the sake of future compatibility and clarity, if nothing else, we
+would strongly recommend you explicitly use the named 'binmode' option
+as shown in the first example.
+
+=head2 eval / evaltt
+
+The 'eval' filter evaluates the block as template text, processing
+any directives embedded within it. This allows template variables to
+contain template fragments, or for some method to be provided for
+returning template fragments from an external source such as a
+database, which can then be processed in the template as required.
+
+ my $vars = {
+ fragment => "The cat sat on the [% place %]",
+ };
+ $template->process($file, $vars);
+
+The following example:
+
+ [% fragment | eval %]
+
+is therefore equivalent to
+
+ The cat sat on the [% place %]
+
+The 'evaltt' filter is provided as an alias for 'eval'.
+
+=head2 perl / evalperl
+
+The 'perl' filter evaluates the block as Perl code. The EVAL_PERL
+option must be set to a true value or a 'perl' exception will be
+thrown.
+
+ [% my_perl_code | perl %]
+
+In most cases, the [% PERL %] ... [% END %] block should suffice for
+evaluating Perl code, given that template directives are processed
+before being evaluate as Perl. Thus, the previous example could have
+been written in the more verbose form:
+
+ [% PERL %]
+ [% my_perl_code %]
+ [% END %]
+
+as well as
+
+ [% FILTER perl %]
+ [% my_perl_code %]
+ [% END %]
+
+The 'evalperl' filter is provided as an alias for 'perl' for backwards
+compatibility.
+
+=head2 stdout(options)
+
+The stdout filter prints the output generated by the enclosing block to
+STDOUT. The 'binmode' option can be passed as either a named parameter
+or a single argument to set STDOUT to binary mode (see the
+binmode perl function).
+
+ [% PROCESS something/cool
+ FILTER stdout(binmode=1) # recommended %]
+
+ [% PROCESS something/cool
+ FILTER stdout(1) # alternate %]
+
+The stdout filter can be used to force binmode on STDOUT, or also inside
+redirect, null or stderr blocks to make sure that particular output goes
+to stdout. See the null filter below for an example.
+
+=head2 stderr
+
+The stderr filter prints the output generated by the enclosing block to
+STDERR.
+
+=head2 null
+
+The null filter prints nothing. This is useful for plugins whose
+methods return values that you don't want to appear in the output.
+Rather than assigning every plugin method call to a dummy variable
+to silence it, you can wrap the block in a null filter:
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0, 255);
+ im.arc(50,50,95,75,0,360,blue);
+ im.fill(50,50,red);
+ im.png | stdout(1);
+ END;
+ -%]
+
+Notice the use of the stdout filter to ensure that a particular expression
+generates output to stdout (in this case in binary mode).
+
+=head2 latex(outputType)
+
+Passes the text block to LaTeX and produces either PDF, DVI or
+PostScript output. The 'outputType' argument determines the output
+format and it should be set to one of the strings: "pdf" (default),
+"dvi", or "ps".
+
+The text block should be a complete LaTeX source file.
+
+ [% FILTER latex("pdf") -%]
+ \documentclass{article}
+
+ \begin{document}
+
+ \title{A Sample TT2 \LaTeX\ Source File}
+ \author{Craig Barratt}
+ \maketitle
+
+ \section{Introduction}
+ This is some text.
+
+ \end{document}
+ [% END -%]
+
+The output will be a PDF file. You should be careful not to prepend or
+append any extraneous characters or text outside the FILTER block,
+since this text will wrap the (binary) output of the latex filter.
+Notice the END directive uses '-%]' for the END_TAG to remove the
+trailing new line.
+
+One example where you might prepend text is in a CGI script where
+you might include the Content-Type before the latex output, eg:
+
+ Content-Type: application/pdf
+
+ [% FILTER latex("pdf") -%]
+ \documentclass{article}
+ \begin{document}
+ ...
+ \end{document}
+ [% END -%]
+
+In other cases you might use the redirect filter to put the output
+into a file, rather than delivering it to stdout. This might be
+suitable for batch scripts:
+
+ [% output = FILTER latex("pdf") -%]
+ \documentclass{article}
+ \begin{document}
+ ...
+ \end{document}
+ [% END; output | redirect("document.pdf", 1) -%]
+
+(Notice the second argument to redirect to force binary mode.)
+
+Note that the latex filter runs one or two external programs, so it
+isn't very fast. But for modest documents the performance is adequate,
+even for interactive applications.
+
+A error of type 'latex' will be thrown if there is an error reported
+by latex, pdflatex or dvips.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.72, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Context|Template::Context>, L<Template::Manual::Filters|Template::Manual::Filters>
diff --git a/lib/Template/Grammar.pm b/lib/Template/Grammar.pm
new file mode 100644
index 0000000..2e1a808
--- /dev/null
+++ b/lib/Template/Grammar.pm
@@ -0,0 +1,6174 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Grammar
+#
+# DESCRIPTION
+# Grammar file for the Template Toolkit language containing token
+# definitions and parser state/rules tables generated by Parse::Yapp.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#------------------------------------------------------------------------
+#
+# NOTE: this module is constructed from the parser/Grammar.pm.skel
+# file by running the parser/yc script. You only need to do this if
+# you have modified the grammar in the parser/Parser.yp file and need
+# to-recompile it. See the README in the 'parser' directory for more
+# information (sub-directory of the Template distribution).
+#
+#------------------------------------------------------------------------
+#
+# $Id: Grammar.pm,v 2.19 2003/04/29 12:47:22 abw Exp $
+#
+#========================================================================
+
+package Template::Grammar;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.19 $ =~ /(\d+)\.(\d+)/);
+
+my (@RESERVED, %CMPOP, $LEXTABLE, $RULES, $STATES);
+my ($factory, $rawstart);
+
+
+#========================================================================
+
+# Reserved words, comparison and binary operators
+#========================================================================
+
+@RESERVED = qw(
+ GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER BLOCK END
+ USE PLUGIN FILTER MACRO PERL RAWPERL TO STEP AND OR NOT DIV MOD
+ IF UNLESS ELSE ELSIF FOR NEXT WHILE SWITCH CASE META IN
+ TRY THROW CATCH FINAL LAST RETURN STOP CLEAR VIEW DEBUG
+ );
+
+# for historical reasons, != and == are converted to ne and eq to perform
+# stringwise comparison (mainly because it doesn't generate "non-numerical
+# comparison" warnings which != and == can) but the others (e.g. < > <= >=)
+# are not converted to their stringwise equivalents. I added 'gt' et al,
+# briefly for v2.04d and then took them out again in 2.04e.
+
+%CMPOP = qw(
+ != ne
+ == eq
+ < <
+ > >
+ >= >=
+ <= <=
+);
+
+
+#========================================================================
+# Lexer Token Table
+#========================================================================
+
+# lookup table used by lexer is initialised with special-cases
+$LEXTABLE = {
+ 'FOREACH' => 'FOR',
+ 'BREAK' => 'LAST',
+ '&&' => 'AND',
+ '||' => 'OR',
+ '!' => 'NOT',
+ '|' => 'FILTER',
+ '.' => 'DOT',
+ '_' => 'CAT',
+ '..' => 'TO',
+# ':' => 'MACRO',
+ '=' => 'ASSIGN',
+ '=>' => 'ASSIGN',
+# '->' => 'ARROW',
+ ',' => 'COMMA',
+ '\\' => 'REF',
+ 'and' => 'AND', # explicitly specified so that qw( and or
+ 'or' => 'OR', # not ) can always be used in lower case,
+ 'not' => 'NOT', # regardless of ANYCASE flag
+ 'mod' => 'MOD',
+ 'div' => 'DIV',
+};
+
+# localise the temporary variables needed to complete lexer table
+{
+# my @tokens = qw< ( ) [ ] { } ${ $ / ; : ? >;
+ my @tokens = qw< ( ) [ ] { } ${ $ + / ; : ? >;
+ my @cmpop = keys %CMPOP;
+# my @binop = qw( + - * % ); # '/' above, in @tokens
+ my @binop = qw( - * % ); # '+' and '/' above, in @tokens
+
+ # fill lexer table, slice by slice, with reserved words and operators
+ @$LEXTABLE{ @RESERVED, @cmpop, @binop, @tokens }
+ = ( @RESERVED, ('CMPOP') x @cmpop, ('BINOP') x @binop, @tokens );
+}
+
+
+#========================================================================
+# CLASS METHODS
+#========================================================================
+
+sub new {
+ my $class = shift;
+ bless {
+ LEXTABLE => $LEXTABLE,
+ STATES => $STATES,
+ RULES => $RULES,
+ }, $class;
+}
+
+# update method to set package-scoped $factory lexical
+sub install_factory {
+ my ($self, $new_factory) = @_;
+ $factory = $new_factory;
+}
+
+
+#========================================================================
+# States
+#========================================================================
+
+$STATES = [
+ {#State 0
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'template' => 52,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'switch' => 34,
+ 'try' => 35,
+ 'assign' => 19,
+ 'block' => 72,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 1
+ ACTIONS => {
+ "\$" => 43,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'setlist' => 76,
+ 'item' => 39,
+ 'assign' => 19,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 2
+ DEFAULT => -130
+ },
+ {#State 3
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 79,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 4
+ DEFAULT => -23
+ },
+ {#State 5
+ ACTIONS => {
+ ";" => 80
+ }
+ },
+ {#State 6
+ DEFAULT => -37
+ },
+ {#State 7
+ DEFAULT => -14
+ },
+ {#State 8
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 90,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 9
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "]" => 94,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 96,
+ 'item' => 39,
+ 'range' => 93,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 95,
+ 'list' => 92,
+ 'lterm' => 56
+ }
+ },
+ {#State 10
+ ACTIONS => {
+ ";" => 97
+ }
+ },
+ {#State 11
+ DEFAULT => -5
+ },
+ {#State 12
+ ACTIONS => {
+ ";" => -20
+ },
+ DEFAULT => -27
+ },
+ {#State 13
+ DEFAULT => -78,
+ GOTOS => {
+ '@5-1' => 98
+ }
+ },
+ {#State 14
+ ACTIONS => {
+ 'IDENT' => 99
+ },
+ DEFAULT => -87,
+ GOTOS => {
+ 'blockargs' => 102,
+ 'metadata' => 101,
+ 'meta' => 100
+ }
+ },
+ {#State 15
+ ACTIONS => {
+ 'IDENT' => 99
+ },
+ GOTOS => {
+ 'metadata' => 103,
+ 'meta' => 100
+ }
+ },
+ {#State 16
+ ACTIONS => {
+ 'DOT' => 104,
+ 'ASSIGN' => 105
+ },
+ DEFAULT => -109
+ },
+ {#State 17
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 106,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 18
+ ACTIONS => {
+ 'IDENT' => 107
+ }
+ },
+ {#State 19
+ DEFAULT => -149
+ },
+ {#State 20
+ DEFAULT => -12
+ },
+ {#State 21
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 108,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'loopvar' => 110,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 109,
+ 'lterm' => 56
+ }
+ },
+ {#State 22
+ DEFAULT => -40
+ },
+ {#State 23
+ DEFAULT => -127
+ },
+ {#State 24
+ DEFAULT => -6
+ },
+ {#State 25
+ ACTIONS => {
+ "\"" => 117,
+ "\$" => 114,
+ 'LITERAL' => 116,
+ 'FILENAME' => 83,
+ 'IDENT' => 111,
+ 'NUMBER' => 84,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 118,
+ 'filename' => 85,
+ 'lvalue' => 112,
+ 'lnameargs' => 115,
+ 'item' => 113,
+ 'name' => 82
+ }
+ },
+ {#State 26
+ DEFAULT => -113
+ },
+ {#State 27
+ ACTIONS => {
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 119
+ }
+ },
+ {#State 28
+ ACTIONS => {
+ 'LITERAL' => 124,
+ 'FILENAME' => 83,
+ 'IDENT' => 120,
+ 'NUMBER' => 84
+ },
+ DEFAULT => -87,
+ GOTOS => {
+ 'blockargs' => 123,
+ 'filepart' => 87,
+ 'filename' => 122,
+ 'blockname' => 121,
+ 'metadata' => 101,
+ 'meta' => 100
+ }
+ },
+ {#State 29
+ DEFAULT => -43
+ },
+ {#State 30
+ ACTIONS => {
+ "\$" => 43,
+ 'LITERAL' => 129,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -119,
+ GOTOS => {
+ 'params' => 128,
+ 'hash' => 125,
+ 'item' => 126,
+ 'param' => 127
+ }
+ },
+ {#State 31
+ DEFAULT => -25
+ },
+ {#State 32
+ ACTIONS => {
+ "\"" => 117,
+ "\$" => 114,
+ 'LITERAL' => 116,
+ 'FILENAME' => 83,
+ 'IDENT' => 111,
+ 'NUMBER' => 84,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 118,
+ 'filename' => 85,
+ 'lvalue' => 112,
+ 'lnameargs' => 130,
+ 'item' => 113,
+ 'name' => 82
+ }
+ },
+ {#State 33
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -2,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 131,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 34
+ DEFAULT => -22
+ },
+ {#State 35
+ DEFAULT => -24
+ },
+ {#State 36
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 132,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 37
+ ACTIONS => {
+ "\"" => 60,
+ "\$" => 43,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ 'REF' => 27,
+ 'NUMBER' => 26,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 133,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77
+ }
+ },
+ {#State 38
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 134,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 39
+ ACTIONS => {
+ "(" => 135
+ },
+ DEFAULT => -128
+ },
+ {#State 40
+ ACTIONS => {
+ ";" => 136
+ }
+ },
+ {#State 41
+ DEFAULT => -38
+ },
+ {#State 42
+ DEFAULT => -11
+ },
+ {#State 43
+ ACTIONS => {
+ 'IDENT' => 137
+ }
+ },
+ {#State 44
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 138,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 45
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 139,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 46
+ DEFAULT => -42
+ },
+ {#State 47
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 140,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 48
+ ACTIONS => {
+ 'IF' => 144,
+ 'FILTER' => 143,
+ 'FOR' => 142,
+ 'WHILE' => 146,
+ 'WRAPPER' => 145,
+ 'UNLESS' => 141
+ }
+ },
+ {#State 49
+ DEFAULT => -39
+ },
+ {#State 50
+ DEFAULT => -10
+ },
+ {#State 51
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 147,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 52
+ ACTIONS => {
+ '' => 148
+ }
+ },
+ {#State 53
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 57,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 149,
+ 'term' => 58,
+ 'expr' => 151,
+ 'assign' => 150,
+ 'lterm' => 56
+ }
+ },
+ {#State 54
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 152,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 55
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 153,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 56
+ DEFAULT => -103
+ },
+ {#State 57
+ ACTIONS => {
+ 'ASSIGN' => 154
+ },
+ DEFAULT => -112
+ },
+ {#State 58
+ DEFAULT => -146
+ },
+ {#State 59
+ DEFAULT => -15
+ },
+ {#State 60
+ DEFAULT => -176,
+ GOTOS => {
+ 'quoted' => 155
+ }
+ },
+ {#State 61
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 156,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 62
+ ACTIONS => {
+ ";" => -16,
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -26
+ },
+ {#State 63
+ DEFAULT => -13
+ },
+ {#State 64
+ DEFAULT => -36
+ },
+ {#State 65
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 167,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 66
+ DEFAULT => -9
+ },
+ {#State 67
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 168,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 68
+ DEFAULT => -104
+ },
+ {#State 69
+ ACTIONS => {
+ "\$" => 43,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'setlist' => 169,
+ 'item' => 39,
+ 'assign' => 19,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 70
+ ACTIONS => {
+ "\$" => 43,
+ 'COMMA' => 171,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -19,
+ GOTOS => {
+ 'item' => 39,
+ 'assign' => 170,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 71
+ DEFAULT => -8
+ },
+ {#State 72
+ DEFAULT => -1
+ },
+ {#State 73
+ DEFAULT => -21
+ },
+ {#State 74
+ ACTIONS => {
+ 'ASSIGN' => 172,
+ 'DOT' => 104
+ }
+ },
+ {#State 75
+ ACTIONS => {
+ 'ASSIGN' => 154
+ }
+ },
+ {#State 76
+ ACTIONS => {
+ "\$" => 43,
+ 'COMMA' => 171,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -30,
+ GOTOS => {
+ 'item' => 39,
+ 'assign' => 170,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 77
+ ACTIONS => {
+ 'DOT' => 104
+ },
+ DEFAULT => -109
+ },
+ {#State 78
+ DEFAULT => -112
+ },
+ {#State 79
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 173,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 80
+ DEFAULT => -7
+ },
+ {#State 81
+ DEFAULT => -173
+ },
+ {#State 82
+ DEFAULT => -166
+ },
+ {#State 83
+ DEFAULT => -172
+ },
+ {#State 84
+ DEFAULT => -174
+ },
+ {#State 85
+ ACTIONS => {
+ 'DOT' => 174
+ },
+ DEFAULT => -168
+ },
+ {#State 86
+ ACTIONS => {
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 175
+ }
+ },
+ {#State 87
+ DEFAULT => -171
+ },
+ {#State 88
+ DEFAULT => -169
+ },
+ {#State 89
+ DEFAULT => -176,
+ GOTOS => {
+ 'quoted' => 176
+ }
+ },
+ {#State 90
+ DEFAULT => -35
+ },
+ {#State 91
+ ACTIONS => {
+ "+" => 177,
+ "(" => 178
+ },
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 179
+ }
+ },
+ {#State 92
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 182,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "]" => 180,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 181,
+ 'lterm' => 56
+ }
+ },
+ {#State 93
+ ACTIONS => {
+ "]" => 183
+ }
+ },
+ {#State 94
+ DEFAULT => -107
+ },
+ {#State 95
+ DEFAULT => -116
+ },
+ {#State 96
+ ACTIONS => {
+ 'TO' => 184
+ },
+ DEFAULT => -104
+ },
+ {#State 97
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 185,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 98
+ ACTIONS => {
+ ";" => 186
+ }
+ },
+ {#State 99
+ ACTIONS => {
+ 'ASSIGN' => 187
+ }
+ },
+ {#State 100
+ DEFAULT => -99
+ },
+ {#State 101
+ ACTIONS => {
+ 'COMMA' => 189,
+ 'IDENT' => 99
+ },
+ DEFAULT => -86,
+ GOTOS => {
+ 'meta' => 188
+ }
+ },
+ {#State 102
+ ACTIONS => {
+ ";" => 190
+ }
+ },
+ {#State 103
+ ACTIONS => {
+ 'COMMA' => 189,
+ 'IDENT' => 99
+ },
+ DEFAULT => -17,
+ GOTOS => {
+ 'meta' => 188
+ }
+ },
+ {#State 104
+ ACTIONS => {
+ "\$" => 43,
+ 'IDENT' => 2,
+ 'NUMBER' => 192,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 191
+ }
+ },
+ {#State 105
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'WRAPPER' => 55,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'FILTER' => 25,
+ 'RETURN' => 64,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 193,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'term' => 58,
+ 'loop' => 4,
+ 'expr' => 195,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'atomdir' => 12,
+ 'mdir' => 194,
+ 'sterm' => 68,
+ 'filter' => 29,
+ 'ident' => 149,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'switch' => 34,
+ 'try' => 35,
+ 'assign' => 19,
+ 'directive' => 196,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 106
+ DEFAULT => -33
+ },
+ {#State 107
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'INCLUDE' => 17,
+ "(" => 198,
+ 'SWITCH' => 54,
+ 'WRAPPER' => 55,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'FILTER' => 25,
+ 'RETURN' => 64,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 193,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'term' => 58,
+ 'loop' => 4,
+ 'expr' => 199,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'atomdir' => 12,
+ 'mdir' => 197,
+ 'sterm' => 68,
+ 'filter' => 29,
+ 'ident' => 149,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'switch' => 34,
+ 'try' => 35,
+ 'assign' => 19,
+ 'directive' => 196,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 108
+ ACTIONS => {
+ 'IN' => 201,
+ 'ASSIGN' => 200
+ },
+ DEFAULT => -130
+ },
+ {#State 109
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 202
+ }
+ },
+ {#State 110
+ ACTIONS => {
+ ";" => 203
+ }
+ },
+ {#State 111
+ ACTIONS => {
+ 'ASSIGN' => -130
+ },
+ DEFAULT => -173
+ },
+ {#State 112
+ ACTIONS => {
+ 'ASSIGN' => 204
+ }
+ },
+ {#State 113
+ DEFAULT => -159
+ },
+ {#State 114
+ ACTIONS => {
+ "\$" => 43,
+ 'IDENT' => 205,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 175
+ }
+ },
+ {#State 115
+ ACTIONS => {
+ ";" => 206
+ }
+ },
+ {#State 116
+ ACTIONS => {
+ 'ASSIGN' => -161
+ },
+ DEFAULT => -169
+ },
+ {#State 117
+ DEFAULT => -176,
+ GOTOS => {
+ 'quoted' => 207
+ }
+ },
+ {#State 118
+ DEFAULT => -158
+ },
+ {#State 119
+ ACTIONS => {
+ 'DOT' => 104
+ },
+ DEFAULT => -110
+ },
+ {#State 120
+ ACTIONS => {
+ 'ASSIGN' => 187
+ },
+ DEFAULT => -173
+ },
+ {#State 121
+ DEFAULT => -83
+ },
+ {#State 122
+ ACTIONS => {
+ 'DOT' => 174
+ },
+ DEFAULT => -84
+ },
+ {#State 123
+ ACTIONS => {
+ ";" => 208
+ }
+ },
+ {#State 124
+ DEFAULT => -85
+ },
+ {#State 125
+ ACTIONS => {
+ "}" => 209
+ }
+ },
+ {#State 126
+ ACTIONS => {
+ 'ASSIGN' => 210
+ }
+ },
+ {#State 127
+ DEFAULT => -122
+ },
+ {#State 128
+ ACTIONS => {
+ "\$" => 43,
+ 'COMMA' => 212,
+ 'LITERAL' => 129,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -118,
+ GOTOS => {
+ 'item' => 126,
+ 'param' => 211
+ }
+ },
+ {#State 129
+ ACTIONS => {
+ 'ASSIGN' => 213
+ }
+ },
+ {#State 130
+ DEFAULT => -73
+ },
+ {#State 131
+ DEFAULT => -4
+ },
+ {#State 132
+ ACTIONS => {
+ ";" => 214
+ }
+ },
+ {#State 133
+ ACTIONS => {
+ "}" => 215
+ }
+ },
+ {#State 134
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -142
+ },
+ {#State 135
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 216
+ }
+ },
+ {#State 136
+ DEFAULT => -76,
+ GOTOS => {
+ '@4-2' => 217
+ }
+ },
+ {#State 137
+ DEFAULT => -132
+ },
+ {#State 138
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 218,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 139
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -29
+ },
+ {#State 140
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -28
+ },
+ {#State 141
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 219,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 142
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 108,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'loopvar' => 220,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 109,
+ 'lterm' => 56
+ }
+ },
+ {#State 143
+ ACTIONS => {
+ "\"" => 117,
+ "\$" => 114,
+ 'LITERAL' => 116,
+ 'FILENAME' => 83,
+ 'IDENT' => 111,
+ 'NUMBER' => 84,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 118,
+ 'filename' => 85,
+ 'lvalue' => 112,
+ 'lnameargs' => 221,
+ 'item' => 113,
+ 'name' => 82
+ }
+ },
+ {#State 144
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 222,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 145
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 223,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 146
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 224,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 147
+ DEFAULT => -41
+ },
+ {#State 148
+ DEFAULT => 0
+ },
+ {#State 149
+ ACTIONS => {
+ 'DOT' => 104,
+ 'ASSIGN' => 172
+ },
+ DEFAULT => -109
+ },
+ {#State 150
+ ACTIONS => {
+ ")" => 225
+ }
+ },
+ {#State 151
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ ")" => 226,
+ 'OR' => 162
+ }
+ },
+ {#State 152
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 227,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 153
+ ACTIONS => {
+ ";" => 228
+ }
+ },
+ {#State 154
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 229,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 155
+ ACTIONS => {
+ "\"" => 234,
+ 'TEXT' => 231,
+ ";" => 233,
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 230,
+ 'quotable' => 232
+ }
+ },
+ {#State 156
+ DEFAULT => -34
+ },
+ {#State 157
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 235,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 158
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 236,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 159
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 237,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 160
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 238,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 161
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 239,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 162
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 240,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 163
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 241,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 164
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 242,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 165
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 243,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 166
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 244,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 167
+ DEFAULT => -32
+ },
+ {#State 168
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 245,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 169
+ ACTIONS => {
+ "\$" => 43,
+ 'COMMA' => 171,
+ 'LITERAL' => 75,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ DEFAULT => -31,
+ GOTOS => {
+ 'item' => 39,
+ 'assign' => 170,
+ 'node' => 23,
+ 'ident' => 74
+ }
+ },
+ {#State 170
+ DEFAULT => -147
+ },
+ {#State 171
+ DEFAULT => -148
+ },
+ {#State 172
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 246,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 173
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 247,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 174
+ ACTIONS => {
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 248
+ }
+ },
+ {#State 175
+ ACTIONS => {
+ 'DOT' => 104
+ },
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 249
+ }
+ },
+ {#State 176
+ ACTIONS => {
+ "\"" => 250,
+ 'TEXT' => 231,
+ ";" => 233,
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 230,
+ 'quotable' => 232
+ }
+ },
+ {#State 177
+ ACTIONS => {
+ "\"" => 89,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'filename' => 85,
+ 'name' => 251
+ }
+ },
+ {#State 178
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 252
+ }
+ },
+ {#State 179
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -163,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 180
+ DEFAULT => -105
+ },
+ {#State 181
+ DEFAULT => -114
+ },
+ {#State 182
+ DEFAULT => -115
+ },
+ {#State 183
+ DEFAULT => -106
+ },
+ {#State 184
+ ACTIONS => {
+ "\"" => 60,
+ "\$" => 43,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ 'REF' => 27,
+ 'NUMBER' => 26,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 259,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77
+ }
+ },
+ {#State 185
+ ACTIONS => {
+ 'FINAL' => 260,
+ 'CATCH' => 262
+ },
+ DEFAULT => -72,
+ GOTOS => {
+ 'final' => 261
+ }
+ },
+ {#State 186
+ ACTIONS => {
+ 'TEXT' => 263
+ }
+ },
+ {#State 187
+ ACTIONS => {
+ "\"" => 266,
+ 'LITERAL' => 265,
+ 'NUMBER' => 264
+ }
+ },
+ {#State 188
+ DEFAULT => -97
+ },
+ {#State 189
+ DEFAULT => -98
+ },
+ {#State 190
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'template' => 267,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 72,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 191
+ DEFAULT => -125
+ },
+ {#State 192
+ DEFAULT => -126
+ },
+ {#State 193
+ ACTIONS => {
+ ";" => 268
+ }
+ },
+ {#State 194
+ DEFAULT => -89
+ },
+ {#State 195
+ ACTIONS => {
+ ";" => -150,
+ "+" => 157,
+ 'LITERAL' => -150,
+ 'IDENT' => -150,
+ 'CAT' => 163,
+ "\$" => -150,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ 'COMMA' => -150,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162,
+ "\${" => -150
+ },
+ DEFAULT => -26
+ },
+ {#State 196
+ DEFAULT => -92
+ },
+ {#State 197
+ DEFAULT => -91
+ },
+ {#State 198
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 57,
+ 'IDENT' => 269,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'margs' => 270,
+ 'node' => 23,
+ 'ident' => 149,
+ 'term' => 58,
+ 'expr' => 151,
+ 'assign' => 150,
+ 'lterm' => 56
+ }
+ },
+ {#State 199
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -26
+ },
+ {#State 200
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 271,
+ 'lterm' => 56
+ }
+ },
+ {#State 201
+ ACTIONS => {
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 272,
+ 'lterm' => 56
+ }
+ },
+ {#State 202
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -64,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 203
+ DEFAULT => -56,
+ GOTOS => {
+ '@1-3' => 273
+ }
+ },
+ {#State 204
+ ACTIONS => {
+ "\"" => 89,
+ "\$" => 86,
+ 'LITERAL' => 88,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'names' => 91,
+ 'nameargs' => 274,
+ 'filename' => 85,
+ 'name' => 82
+ }
+ },
+ {#State 205
+ ACTIONS => {
+ 'ASSIGN' => -132
+ },
+ DEFAULT => -130
+ },
+ {#State 206
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 275,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 207
+ ACTIONS => {
+ "\"" => 276,
+ 'TEXT' => 231,
+ ";" => 233,
+ "\$" => 43,
+ 'IDENT' => 2,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 230,
+ 'quotable' => 232
+ }
+ },
+ {#State 208
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 277,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 209
+ DEFAULT => -108
+ },
+ {#State 210
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 278,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 211
+ DEFAULT => -120
+ },
+ {#State 212
+ DEFAULT => -121
+ },
+ {#State 213
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 279,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 214
+ DEFAULT => -74,
+ GOTOS => {
+ '@3-3' => 280
+ }
+ },
+ {#State 215
+ DEFAULT => -131
+ },
+ {#State 216
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ ")" => 281,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 217
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 282,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 218
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 283,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 219
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -47
+ },
+ {#State 220
+ DEFAULT => -58
+ },
+ {#State 221
+ DEFAULT => -81
+ },
+ {#State 222
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -45
+ },
+ {#State 223
+ DEFAULT => -66
+ },
+ {#State 224
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -61
+ },
+ {#State 225
+ DEFAULT => -144
+ },
+ {#State 226
+ DEFAULT => -145
+ },
+ {#State 227
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 284,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 228
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 285,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 229
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -151
+ },
+ {#State 230
+ ACTIONS => {
+ 'DOT' => 104
+ },
+ DEFAULT => -177
+ },
+ {#State 231
+ DEFAULT => -178
+ },
+ {#State 232
+ DEFAULT => -175
+ },
+ {#State 233
+ DEFAULT => -179
+ },
+ {#State 234
+ DEFAULT => -111
+ },
+ {#State 235
+ ACTIONS => {
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166
+ },
+ DEFAULT => -135
+ },
+ {#State 236
+ ACTIONS => {
+ ":" => 286,
+ 'CMPOP' => 164,
+ "?" => 158,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 237
+ ACTIONS => {
+ 'MOD' => 165
+ },
+ DEFAULT => -136
+ },
+ {#State 238
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -140
+ },
+ {#State 239
+ ACTIONS => {
+ "+" => 157,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166
+ },
+ DEFAULT => -133
+ },
+ {#State 240
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -141
+ },
+ {#State 241
+ ACTIONS => {
+ "+" => 157,
+ 'CMPOP' => 164,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -139
+ },
+ {#State 242
+ ACTIONS => {
+ "+" => 157,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'BINOP' => 161
+ },
+ DEFAULT => -138
+ },
+ {#State 243
+ DEFAULT => -137
+ },
+ {#State 244
+ ACTIONS => {
+ 'DIV' => 159,
+ 'MOD' => 165
+ },
+ DEFAULT => -134
+ },
+ {#State 245
+ DEFAULT => -59,
+ GOTOS => {
+ '@2-3' => 287
+ }
+ },
+ {#State 246
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -150
+ },
+ {#State 247
+ ACTIONS => {
+ 'ELSIF' => 290,
+ 'ELSE' => 288
+ },
+ DEFAULT => -50,
+ GOTOS => {
+ 'else' => 289
+ }
+ },
+ {#State 248
+ DEFAULT => -170
+ },
+ {#State 249
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -162,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 250
+ DEFAULT => -167
+ },
+ {#State 251
+ DEFAULT => -165
+ },
+ {#State 252
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ ")" => 291,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 253
+ ACTIONS => {
+ 'DOT' => 104,
+ 'ASSIGN' => 292
+ },
+ DEFAULT => -109
+ },
+ {#State 254
+ ACTIONS => {
+ "(" => 135,
+ 'ASSIGN' => 210
+ },
+ DEFAULT => -128
+ },
+ {#State 255
+ DEFAULT => -153
+ },
+ {#State 256
+ ACTIONS => {
+ 'ASSIGN' => 213
+ },
+ DEFAULT => -112
+ },
+ {#State 257
+ DEFAULT => -152
+ },
+ {#State 258
+ DEFAULT => -155
+ },
+ {#State 259
+ DEFAULT => -117
+ },
+ {#State 260
+ ACTIONS => {
+ ";" => 293
+ }
+ },
+ {#State 261
+ ACTIONS => {
+ 'END' => 294
+ }
+ },
+ {#State 262
+ ACTIONS => {
+ ";" => 296,
+ 'DEFAULT' => 297,
+ 'FILENAME' => 83,
+ 'IDENT' => 81,
+ 'NUMBER' => 84
+ },
+ GOTOS => {
+ 'filepart' => 87,
+ 'filename' => 295
+ }
+ },
+ {#State 263
+ ACTIONS => {
+ 'END' => 298
+ }
+ },
+ {#State 264
+ DEFAULT => -102
+ },
+ {#State 265
+ DEFAULT => -100
+ },
+ {#State 266
+ ACTIONS => {
+ 'TEXT' => 299
+ }
+ },
+ {#State 267
+ ACTIONS => {
+ 'END' => 300
+ }
+ },
+ {#State 268
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 301,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 269
+ ACTIONS => {
+ 'COMMA' => -96,
+ 'IDENT' => -96,
+ ")" => -96
+ },
+ DEFAULT => -130
+ },
+ {#State 270
+ ACTIONS => {
+ 'COMMA' => 304,
+ 'IDENT' => 302,
+ ")" => 303
+ }
+ },
+ {#State 271
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 305
+ }
+ },
+ {#State 272
+ DEFAULT => -156,
+ GOTOS => {
+ 'args' => 306
+ }
+ },
+ {#State 273
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 307,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 274
+ DEFAULT => -157
+ },
+ {#State 275
+ ACTIONS => {
+ 'END' => 308
+ }
+ },
+ {#State 276
+ ACTIONS => {
+ 'ASSIGN' => -160
+ },
+ DEFAULT => -167
+ },
+ {#State 277
+ ACTIONS => {
+ 'END' => 309
+ }
+ },
+ {#State 278
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -124
+ },
+ {#State 279
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -123
+ },
+ {#State 280
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 310,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 281
+ DEFAULT => -129
+ },
+ {#State 282
+ ACTIONS => {
+ 'END' => 311
+ }
+ },
+ {#State 283
+ ACTIONS => {
+ 'ELSIF' => 290,
+ 'ELSE' => 288
+ },
+ DEFAULT => -50,
+ GOTOS => {
+ 'else' => 312
+ }
+ },
+ {#State 284
+ ACTIONS => {
+ 'CASE' => 313
+ },
+ DEFAULT => -55,
+ GOTOS => {
+ 'case' => 314
+ }
+ },
+ {#State 285
+ ACTIONS => {
+ 'END' => 315
+ }
+ },
+ {#State 286
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 316,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 287
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 317,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 288
+ ACTIONS => {
+ ";" => 318
+ }
+ },
+ {#State 289
+ ACTIONS => {
+ 'END' => 319
+ }
+ },
+ {#State 290
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 320,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 291
+ DEFAULT => -164
+ },
+ {#State 292
+ ACTIONS => {
+ 'NOT' => 38,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "(" => 53,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'expr' => 321,
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 58,
+ 'lterm' => 56
+ }
+ },
+ {#State 293
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 322,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 294
+ DEFAULT => -67
+ },
+ {#State 295
+ ACTIONS => {
+ 'DOT' => 174,
+ ";" => 323
+ }
+ },
+ {#State 296
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 324,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 297
+ ACTIONS => {
+ ";" => 325
+ }
+ },
+ {#State 298
+ DEFAULT => -79
+ },
+ {#State 299
+ ACTIONS => {
+ "\"" => 326
+ }
+ },
+ {#State 300
+ DEFAULT => -82
+ },
+ {#State 301
+ ACTIONS => {
+ 'END' => 327
+ }
+ },
+ {#State 302
+ DEFAULT => -94
+ },
+ {#State 303
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'WRAPPER' => 55,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'FILTER' => 25,
+ 'RETURN' => 64,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 193,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'term' => 58,
+ 'loop' => 4,
+ 'expr' => 199,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'atomdir' => 12,
+ 'mdir' => 328,
+ 'sterm' => 68,
+ 'filter' => 29,
+ 'ident' => 149,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'switch' => 34,
+ 'try' => 35,
+ 'assign' => 19,
+ 'directive' => 196,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 304
+ DEFAULT => -95
+ },
+ {#State 305
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -62,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 306
+ ACTIONS => {
+ "{" => 30,
+ 'COMMA' => 258,
+ 'LITERAL' => 256,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ DEFAULT => -63,
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 254,
+ 'param' => 255,
+ 'node' => 23,
+ 'ident' => 253,
+ 'term' => 257,
+ 'lterm' => 56
+ }
+ },
+ {#State 307
+ ACTIONS => {
+ 'END' => 329
+ }
+ },
+ {#State 308
+ DEFAULT => -80
+ },
+ {#State 309
+ DEFAULT => -88
+ },
+ {#State 310
+ ACTIONS => {
+ 'END' => 330
+ }
+ },
+ {#State 311
+ DEFAULT => -77
+ },
+ {#State 312
+ ACTIONS => {
+ 'END' => 331
+ }
+ },
+ {#State 313
+ ACTIONS => {
+ ";" => 332,
+ 'DEFAULT' => 334,
+ "{" => 30,
+ 'LITERAL' => 78,
+ 'IDENT' => 2,
+ "\"" => 60,
+ "\$" => 43,
+ "[" => 9,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ "\${" => 37
+ },
+ GOTOS => {
+ 'sterm' => 68,
+ 'item' => 39,
+ 'node' => 23,
+ 'ident' => 77,
+ 'term' => 333,
+ 'lterm' => 56
+ }
+ },
+ {#State 314
+ ACTIONS => {
+ 'END' => 335
+ }
+ },
+ {#State 315
+ DEFAULT => -65
+ },
+ {#State 316
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -143
+ },
+ {#State 317
+ ACTIONS => {
+ 'END' => 336
+ }
+ },
+ {#State 318
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 337,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 319
+ DEFAULT => -46
+ },
+ {#State 320
+ ACTIONS => {
+ 'CMPOP' => 164,
+ "?" => 158,
+ ";" => 338,
+ "+" => 157,
+ 'MOD' => 165,
+ 'DIV' => 159,
+ "/" => 166,
+ 'AND' => 160,
+ 'CAT' => 163,
+ 'BINOP' => 161,
+ 'OR' => 162
+ }
+ },
+ {#State 321
+ ACTIONS => {
+ "+" => 157,
+ 'CAT' => 163,
+ 'CMPOP' => 164,
+ "?" => 158,
+ 'DIV' => 159,
+ 'MOD' => 165,
+ "/" => 166,
+ 'AND' => 160,
+ 'BINOP' => 161,
+ 'OR' => 162
+ },
+ DEFAULT => -154
+ },
+ {#State 322
+ DEFAULT => -71
+ },
+ {#State 323
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 339,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 324
+ ACTIONS => {
+ 'FINAL' => 260,
+ 'CATCH' => 262
+ },
+ DEFAULT => -72,
+ GOTOS => {
+ 'final' => 340
+ }
+ },
+ {#State 325
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 341,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 326
+ DEFAULT => -101
+ },
+ {#State 327
+ DEFAULT => -93
+ },
+ {#State 328
+ DEFAULT => -90
+ },
+ {#State 329
+ DEFAULT => -57
+ },
+ {#State 330
+ DEFAULT => -75
+ },
+ {#State 331
+ DEFAULT => -44
+ },
+ {#State 332
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 342,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 333
+ ACTIONS => {
+ ";" => 343
+ }
+ },
+ {#State 334
+ ACTIONS => {
+ ";" => 344
+ }
+ },
+ {#State 335
+ DEFAULT => -51
+ },
+ {#State 336
+ DEFAULT => -60
+ },
+ {#State 337
+ DEFAULT => -49
+ },
+ {#State 338
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 345,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 339
+ ACTIONS => {
+ 'FINAL' => 260,
+ 'CATCH' => 262
+ },
+ DEFAULT => -72,
+ GOTOS => {
+ 'final' => 346
+ }
+ },
+ {#State 340
+ DEFAULT => -70
+ },
+ {#State 341
+ ACTIONS => {
+ 'FINAL' => 260,
+ 'CATCH' => 262
+ },
+ DEFAULT => -72,
+ GOTOS => {
+ 'final' => 347
+ }
+ },
+ {#State 342
+ DEFAULT => -54
+ },
+ {#State 343
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 348,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 344
+ ACTIONS => {
+ 'SET' => 1,
+ 'PERL' => 40,
+ 'NOT' => 38,
+ 'IDENT' => 2,
+ 'CLEAR' => 41,
+ 'UNLESS' => 3,
+ 'IF' => 44,
+ "\$" => 43,
+ 'STOP' => 6,
+ 'CALL' => 45,
+ 'THROW' => 8,
+ 'GET' => 47,
+ "[" => 9,
+ 'TRY' => 10,
+ 'LAST' => 49,
+ 'DEBUG' => 51,
+ 'RAWPERL' => 13,
+ 'META' => 15,
+ 'INCLUDE' => 17,
+ "(" => 53,
+ 'SWITCH' => 54,
+ 'MACRO' => 18,
+ 'WRAPPER' => 55,
+ ";" => -18,
+ 'FOR' => 21,
+ 'NEXT' => 22,
+ 'LITERAL' => 57,
+ 'TEXT' => 24,
+ "\"" => 60,
+ 'PROCESS' => 61,
+ 'RETURN' => 64,
+ 'FILTER' => 25,
+ 'INSERT' => 65,
+ 'NUMBER' => 26,
+ 'REF' => 27,
+ 'WHILE' => 67,
+ 'BLOCK' => 28,
+ 'DEFAULT' => 69,
+ "{" => 30,
+ 'USE' => 32,
+ 'VIEW' => 36,
+ "\${" => 37
+ },
+ DEFAULT => -3,
+ GOTOS => {
+ 'item' => 39,
+ 'node' => 23,
+ 'rawperl' => 59,
+ 'term' => 58,
+ 'loop' => 4,
+ 'use' => 63,
+ 'expr' => 62,
+ 'capture' => 42,
+ 'statement' => 5,
+ 'view' => 7,
+ 'wrapper' => 46,
+ 'atomexpr' => 48,
+ 'chunk' => 11,
+ 'defblock' => 66,
+ 'atomdir' => 12,
+ 'anonblock' => 50,
+ 'sterm' => 68,
+ 'defblockname' => 14,
+ 'filter' => 29,
+ 'ident' => 16,
+ 'perl' => 31,
+ 'setlist' => 70,
+ 'chunks' => 33,
+ 'try' => 35,
+ 'switch' => 34,
+ 'assign' => 19,
+ 'block' => 349,
+ 'directive' => 71,
+ 'macro' => 20,
+ 'condition' => 73,
+ 'lterm' => 56
+ }
+ },
+ {#State 345
+ ACTIONS => {
+ 'ELSIF' => 290,
+ 'ELSE' => 288
+ },
+ DEFAULT => -50,
+ GOTOS => {
+ 'else' => 350
+ }
+ },
+ {#State 346
+ DEFAULT => -68
+ },
+ {#State 347
+ DEFAULT => -69
+ },
+ {#State 348
+ ACTIONS => {
+ 'CASE' => 313
+ },
+ DEFAULT => -55,
+ GOTOS => {
+ 'case' => 351
+ }
+ },
+ {#State 349
+ DEFAULT => -53
+ },
+ {#State 350
+ DEFAULT => -48
+ },
+ {#State 351
+ DEFAULT => -52
+ }
+];
+
+
+#========================================================================
+# Rules
+#========================================================================
+
+$RULES = [
+ [#Rule 0
+ '$start', 2, undef
+ ],
+ [#Rule 1
+ 'template', 1,
+sub
+#line 64 "Parser.yp"
+{ $factory->template($_[1]) }
+ ],
+ [#Rule 2
+ 'block', 1,
+sub
+#line 67 "Parser.yp"
+{ $factory->block($_[1]) }
+ ],
+ [#Rule 3
+ 'block', 0,
+sub
+#line 68 "Parser.yp"
+{ $factory->block() }
+ ],
+ [#Rule 4
+ 'chunks', 2,
+sub
+#line 71 "Parser.yp"
+{ push(@{$_[1]}, $_[2])
+ if defined $_[2]; $_[1] }
+ ],
+ [#Rule 5
+ 'chunks', 1,
+sub
+#line 73 "Parser.yp"
+{ defined $_[1] ? [ $_[1] ] : [ ] }
+ ],
+ [#Rule 6
+ 'chunk', 1,
+sub
+#line 76 "Parser.yp"
+{ $factory->textblock($_[1]) }
+ ],
+ [#Rule 7
+ 'chunk', 2, undef
+ ],
+ [#Rule 8
+ 'statement', 1, undef
+ ],
+ [#Rule 9
+ 'statement', 1, undef
+ ],
+ [#Rule 10
+ 'statement', 1, undef
+ ],
+ [#Rule 11
+ 'statement', 1, undef
+ ],
+ [#Rule 12
+ 'statement', 1, undef
+ ],
+ [#Rule 13
+ 'statement', 1, undef
+ ],
+ [#Rule 14
+ 'statement', 1, undef
+ ],
+ [#Rule 15
+ 'statement', 1, undef
+ ],
+ [#Rule 16
+ 'statement', 1,
+sub
+#line 89 "Parser.yp"
+{ $factory->get($_[1]) }
+ ],
+ [#Rule 17
+ 'statement', 2,
+sub
+#line 90 "Parser.yp"
+{ $_[0]->add_metadata($_[2]); }
+ ],
+ [#Rule 18
+ 'statement', 0, undef
+ ],
+ [#Rule 19
+ 'directive', 1,
+sub
+#line 94 "Parser.yp"
+{ $factory->set($_[1]) }
+ ],
+ [#Rule 20
+ 'directive', 1, undef
+ ],
+ [#Rule 21
+ 'directive', 1, undef
+ ],
+ [#Rule 22
+ 'directive', 1, undef
+ ],
+ [#Rule 23
+ 'directive', 1, undef
+ ],
+ [#Rule 24
+ 'directive', 1, undef
+ ],
+ [#Rule 25
+ 'directive', 1, undef
+ ],
+ [#Rule 26
+ 'atomexpr', 1,
+sub
+#line 108 "Parser.yp"
+{ $factory->get($_[1]) }
+ ],
+ [#Rule 27
+ 'atomexpr', 1, undef
+ ],
+ [#Rule 28
+ 'atomdir', 2,
+sub
+#line 112 "Parser.yp"
+{ $factory->get($_[2]) }
+ ],
+ [#Rule 29
+ 'atomdir', 2,
+sub
+#line 113 "Parser.yp"
+{ $factory->call($_[2]) }
+ ],
+ [#Rule 30
+ 'atomdir', 2,
+sub
+#line 114 "Parser.yp"
+{ $factory->set($_[2]) }
+ ],
+ [#Rule 31
+ 'atomdir', 2,
+sub
+#line 115 "Parser.yp"
+{ $factory->default($_[2]) }
+ ],
+ [#Rule 32
+ 'atomdir', 2,
+sub
+#line 116 "Parser.yp"
+{ $factory->insert($_[2]) }
+ ],
+ [#Rule 33
+ 'atomdir', 2,
+sub
+#line 117 "Parser.yp"
+{ $factory->include($_[2]) }
+ ],
+ [#Rule 34
+ 'atomdir', 2,
+sub
+#line 118 "Parser.yp"
+{ $factory->process($_[2]) }
+ ],
+ [#Rule 35
+ 'atomdir', 2,
+sub
+#line 119 "Parser.yp"
+{ $factory->throw($_[2]) }
+ ],
+ [#Rule 36
+ 'atomdir', 1,
+sub
+#line 120 "Parser.yp"
+{ $factory->return() }
+ ],
+ [#Rule 37
+ 'atomdir', 1,
+sub
+#line 121 "Parser.yp"
+{ $factory->stop() }
+ ],
+ [#Rule 38
+ 'atomdir', 1,
+sub
+#line 122 "Parser.yp"
+{ "\$output = '';"; }
+ ],
+ [#Rule 39
+ 'atomdir', 1,
+sub
+#line 123 "Parser.yp"
+{ $_[0]->{ INFOR } || $_[0]->{ INWHILE }
+ ? 'last LOOP;'
+ : 'last;' }
+ ],
+ [#Rule 40
+ 'atomdir', 1,
+sub
+#line 126 "Parser.yp"
+{ $_[0]->{ INFOR }
+ ? $factory->next()
+ : ($_[0]->{ INWHILE }
+ ? 'next LOOP;'
+ : 'next;') }
+ ],
+ [#Rule 41
+ 'atomdir', 2,
+sub
+#line 131 "Parser.yp"
+{ if ($_[2]->[0]->[0] =~ /^'(on|off)'$/) {
+ $_[0]->{ DEBUG_DIRS } = ($1 eq 'on');
+ $factory->debug($_[2]);
+ }
+ else {
+ $_[0]->{ DEBUG_DIRS } ? $factory->debug($_[2]) : '';
+ }
+ }
+ ],
+ [#Rule 42
+ 'atomdir', 1, undef
+ ],
+ [#Rule 43
+ 'atomdir', 1, undef
+ ],
+ [#Rule 44
+ 'condition', 6,
+sub
+#line 144 "Parser.yp"
+{ $factory->if(@_[2, 4, 5]) }
+ ],
+ [#Rule 45
+ 'condition', 3,
+sub
+#line 145 "Parser.yp"
+{ $factory->if(@_[3, 1]) }
+ ],
+ [#Rule 46
+ 'condition', 6,
+sub
+#line 147 "Parser.yp"
+{ $factory->if("!($_[2])", @_[4, 5]) }
+ ],
+ [#Rule 47
+ 'condition', 3,
+sub
+#line 148 "Parser.yp"
+{ $factory->if("!($_[3])", $_[1]) }
+ ],
+ [#Rule 48
+ 'else', 5,
+sub
+#line 152 "Parser.yp"
+{ unshift(@{$_[5]}, [ @_[2, 4] ]);
+ $_[5]; }
+ ],
+ [#Rule 49
+ 'else', 3,
+sub
+#line 154 "Parser.yp"
+{ [ $_[3] ] }
+ ],
+ [#Rule 50
+ 'else', 0,
+sub
+#line 155 "Parser.yp"
+{ [ undef ] }
+ ],
+ [#Rule 51
+ 'switch', 6,
+sub
+#line 159 "Parser.yp"
+{ $factory->switch(@_[2, 5]) }
+ ],
+ [#Rule 52
+ 'case', 5,
+sub
+#line 163 "Parser.yp"
+{ unshift(@{$_[5]}, [ @_[2, 4] ]);
+ $_[5]; }
+ ],
+ [#Rule 53
+ 'case', 4,
+sub
+#line 165 "Parser.yp"
+{ [ $_[4] ] }
+ ],
+ [#Rule 54
+ 'case', 3,
+sub
+#line 166 "Parser.yp"
+{ [ $_[3] ] }
+ ],
+ [#Rule 55
+ 'case', 0,
+sub
+#line 167 "Parser.yp"
+{ [ undef ] }
+ ],
+ [#Rule 56
+ '@1-3', 0,
+sub
+#line 170 "Parser.yp"
+{ $_[0]->{ INFOR }++ }
+ ],
+ [#Rule 57
+ 'loop', 6,
+sub
+#line 171 "Parser.yp"
+{ $_[0]->{ INFOR }--;
+ $factory->foreach(@{$_[2]}, $_[5]) }
+ ],
+ [#Rule 58
+ 'loop', 3,
+sub
+#line 175 "Parser.yp"
+{ $factory->foreach(@{$_[3]}, $_[1]) }
+ ],
+ [#Rule 59
+ '@2-3', 0,
+sub
+#line 176 "Parser.yp"
+{ $_[0]->{ INWHILE }++ }
+ ],
+ [#Rule 60
+ 'loop', 6,
+sub
+#line 177 "Parser.yp"
+{ $_[0]->{ INWHILE }--;
+ $factory->while(@_[2, 5]) }
+ ],
+ [#Rule 61
+ 'loop', 3,
+sub
+#line 179 "Parser.yp"
+{ $factory->while(@_[3, 1]) }
+ ],
+ [#Rule 62
+ 'loopvar', 4,
+sub
+#line 182 "Parser.yp"
+{ [ @_[1, 3, 4] ] }
+ ],
+ [#Rule 63
+ 'loopvar', 4,
+sub
+#line 183 "Parser.yp"
+{ [ @_[1, 3, 4] ] }
+ ],
+ [#Rule 64
+ 'loopvar', 2,
+sub
+#line 184 "Parser.yp"
+{ [ 0, @_[1, 2] ] }
+ ],
+ [#Rule 65
+ 'wrapper', 5,
+sub
+#line 188 "Parser.yp"
+{ $factory->wrapper(@_[2, 4]) }
+ ],
+ [#Rule 66
+ 'wrapper', 3,
+sub
+#line 190 "Parser.yp"
+{ $factory->wrapper(@_[3, 1]) }
+ ],
+ [#Rule 67
+ 'try', 5,
+sub
+#line 194 "Parser.yp"
+{ $factory->try(@_[3, 4]) }
+ ],
+ [#Rule 68
+ 'final', 5,
+sub
+#line 198 "Parser.yp"
+{ unshift(@{$_[5]}, [ @_[2,4] ]);
+ $_[5]; }
+ ],
+ [#Rule 69
+ 'final', 5,
+sub
+#line 201 "Parser.yp"
+{ unshift(@{$_[5]}, [ undef, $_[4] ]);
+ $_[5]; }
+ ],
+ [#Rule 70
+ 'final', 4,
+sub
+#line 204 "Parser.yp"
+{ unshift(@{$_[4]}, [ undef, $_[3] ]);
+ $_[4]; }
+ ],
+ [#Rule 71
+ 'final', 3,
+sub
+#line 206 "Parser.yp"
+{ [ $_[3] ] }
+ ],
+ [#Rule 72
+ 'final', 0,
+sub
+#line 207 "Parser.yp"
+{ [ 0 ] }
+ ],
+ [#Rule 73
+ 'use', 2,
+sub
+#line 210 "Parser.yp"
+{ $factory->use($_[2]) }
+ ],
+ [#Rule 74
+ '@3-3', 0,
+sub
+#line 213 "Parser.yp"
+{ $_[0]->push_defblock(); }
+ ],
+ [#Rule 75
+ 'view', 6,
+sub
+#line 214 "Parser.yp"
+{ $factory->view(@_[2,5],
+ $_[0]->pop_defblock) }
+ ],
+ [#Rule 76
+ '@4-2', 0,
+sub
+#line 218 "Parser.yp"
+{ ${$_[0]->{ INPERL }}++; }
+ ],
+ [#Rule 77
+ 'perl', 5,
+sub
+#line 219 "Parser.yp"
+{ ${$_[0]->{ INPERL }}--;
+ $_[0]->{ EVAL_PERL }
+ ? $factory->perl($_[4])
+ : $factory->no_perl(); }
+ ],
+ [#Rule 78
+ '@5-1', 0,
+sub
+#line 225 "Parser.yp"
+{ ${$_[0]->{ INPERL }}++;
+ $rawstart = ${$_[0]->{'LINE'}}; }
+ ],
+ [#Rule 79
+ 'rawperl', 5,
+sub
+#line 227 "Parser.yp"
+{ ${$_[0]->{ INPERL }}--;
+ $_[0]->{ EVAL_PERL }
+ ? $factory->rawperl($_[4], $rawstart)
+ : $factory->no_perl(); }
+ ],
+ [#Rule 80
+ 'filter', 5,
+sub
+#line 234 "Parser.yp"
+{ $factory->filter(@_[2,4]) }
+ ],
+ [#Rule 81
+ 'filter', 3,
+sub
+#line 236 "Parser.yp"
+{ $factory->filter(@_[3,1]) }
+ ],
+ [#Rule 82
+ 'defblock', 5,
+sub
+#line 241 "Parser.yp"
+{ my $name = join('/', @{ $_[0]->{ DEFBLOCKS } });
+ pop(@{ $_[0]->{ DEFBLOCKS } });
+ $_[0]->define_block($name, $_[4]);
+ undef
+ }
+ ],
+ [#Rule 83
+ 'defblockname', 2,
+sub
+#line 248 "Parser.yp"
+{ push(@{ $_[0]->{ DEFBLOCKS } }, $_[2]);
+ $_[2];
+ }
+ ],
+ [#Rule 84
+ 'blockname', 1, undef
+ ],
+ [#Rule 85
+ 'blockname', 1,
+sub
+#line 254 "Parser.yp"
+{ $_[1] =~ s/^'(.*)'$/$1/; $_[1] }
+ ],
+ [#Rule 86
+ 'blockargs', 1, undef
+ ],
+ [#Rule 87
+ 'blockargs', 0, undef
+ ],
+ [#Rule 88
+ 'anonblock', 5,
+sub
+#line 262 "Parser.yp"
+{ local $" = ', ';
+ print STDERR "experimental block args: [@{ $_[2] }]\n"
+ if $_[2];
+ $factory->anon_block($_[4]) }
+ ],
+ [#Rule 89
+ 'capture', 3,
+sub
+#line 268 "Parser.yp"
+{ $factory->capture(@_[1, 3]) }
+ ],
+ [#Rule 90
+ 'macro', 6,
+sub
+#line 272 "Parser.yp"
+{ $factory->macro(@_[2, 6, 4]) }
+ ],
+ [#Rule 91
+ 'macro', 3,
+sub
+#line 273 "Parser.yp"
+{ $factory->macro(@_[2, 3]) }
+ ],
+ [#Rule 92
+ 'mdir', 1, undef
+ ],
+ [#Rule 93
+ 'mdir', 4,
+sub
+#line 277 "Parser.yp"
+{ $_[3] }
+ ],
+ [#Rule 94
+ 'margs', 2,
+sub
+#line 280 "Parser.yp"
+{ push(@{$_[1]}, $_[2]); $_[1] }
+ ],
+ [#Rule 95
+ 'margs', 2,
+sub
+#line 281 "Parser.yp"
+{ $_[1] }
+ ],
+ [#Rule 96
+ 'margs', 1,
+sub
+#line 282 "Parser.yp"
+{ [ $_[1] ] }
+ ],
+ [#Rule 97
+ 'metadata', 2,
+sub
+#line 285 "Parser.yp"
+{ push(@{$_[1]}, @{$_[2]}); $_[1] }
+ ],
+ [#Rule 98
+ 'metadata', 2, undef
+ ],
+ [#Rule 99
+ 'metadata', 1, undef
+ ],
+ [#Rule 100
+ 'meta', 3,
+sub
+#line 290 "Parser.yp"
+{ for ($_[3]) { s/^'//; s/'$//;
+ s/\\'/'/g };
+ [ @_[1,3] ] }
+ ],
+ [#Rule 101
+ 'meta', 5,
+sub
+#line 293 "Parser.yp"
+{ [ @_[1,4] ] }
+ ],
+ [#Rule 102
+ 'meta', 3,
+sub
+#line 294 "Parser.yp"
+{ [ @_[1,3] ] }
+ ],
+ [#Rule 103
+ 'term', 1, undef
+ ],
+ [#Rule 104
+ 'term', 1, undef
+ ],
+ [#Rule 105
+ 'lterm', 3,
+sub
+#line 306 "Parser.yp"
+{ "[ $_[2] ]" }
+ ],
+ [#Rule 106
+ 'lterm', 3,
+sub
+#line 307 "Parser.yp"
+{ "[ $_[2] ]" }
+ ],
+ [#Rule 107
+ 'lterm', 2,
+sub
+#line 308 "Parser.yp"
+{ "[ ]" }
+ ],
+ [#Rule 108
+ 'lterm', 3,
+sub
+#line 309 "Parser.yp"
+{ "{ $_[2] }" }
+ ],
+ [#Rule 109
+ 'sterm', 1,
+sub
+#line 312 "Parser.yp"
+{ $factory->ident($_[1]) }
+ ],
+ [#Rule 110
+ 'sterm', 2,
+sub
+#line 313 "Parser.yp"
+{ $factory->identref($_[2]) }
+ ],
+ [#Rule 111
+ 'sterm', 3,
+sub
+#line 314 "Parser.yp"
+{ $factory->quoted($_[2]) }
+ ],
+ [#Rule 112
+ 'sterm', 1, undef
+ ],
+ [#Rule 113
+ 'sterm', 1, undef
+ ],
+ [#Rule 114
+ 'list', 2,
+sub
+#line 319 "Parser.yp"
+{ "$_[1], $_[2]" }
+ ],
+ [#Rule 115
+ 'list', 2, undef
+ ],
+ [#Rule 116
+ 'list', 1, undef
+ ],
+ [#Rule 117
+ 'range', 3,
+sub
+#line 324 "Parser.yp"
+{ $_[1] . '..' . $_[3] }
+ ],
+ [#Rule 118
+ 'hash', 1, undef
+ ],
+ [#Rule 119
+ 'hash', 0,
+sub
+#line 329 "Parser.yp"
+{ "" }
+ ],
+ [#Rule 120
+ 'params', 2,
+sub
+#line 332 "Parser.yp"
+{ "$_[1], $_[2]" }
+ ],
+ [#Rule 121
+ 'params', 2, undef
+ ],
+ [#Rule 122
+ 'params', 1, undef
+ ],
+ [#Rule 123
+ 'param', 3,
+sub
+#line 337 "Parser.yp"
+{ "$_[1] => $_[3]" }
+ ],
+ [#Rule 124
+ 'param', 3,
+sub
+#line 338 "Parser.yp"
+{ "$_[1] => $_[3]" }
+ ],
+ [#Rule 125
+ 'ident', 3,
+sub
+#line 341 "Parser.yp"
+{ push(@{$_[1]}, @{$_[3]}); $_[1] }
+ ],
+ [#Rule 126
+ 'ident', 3,
+sub
+#line 342 "Parser.yp"
+{ push(@{$_[1]},
+ map {($_, 0)} split(/\./, $_[3]));
+ $_[1]; }
+ ],
+ [#Rule 127
+ 'ident', 1, undef
+ ],
+ [#Rule 128
+ 'node', 1,
+sub
+#line 348 "Parser.yp"
+{ [ $_[1], 0 ] }
+ ],
+ [#Rule 129
+ 'node', 4,
+sub
+#line 349 "Parser.yp"
+{ [ $_[1], $factory->args($_[3]) ] }
+ ],
+ [#Rule 130
+ 'item', 1,
+sub
+#line 352 "Parser.yp"
+{ "'$_[1]'" }
+ ],
+ [#Rule 131
+ 'item', 3,
+sub
+#line 353 "Parser.yp"
+{ $_[2] }
+ ],
+ [#Rule 132
+ 'item', 2,
+sub
+#line 354 "Parser.yp"
+{ $_[0]->{ V1DOLLAR }
+ ? "'$_[2]'"
+ : $factory->ident(["'$_[2]'", 0]) }
+ ],
+ [#Rule 133
+ 'expr', 3,
+sub
+#line 359 "Parser.yp"
+{ "$_[1] $_[2] $_[3]" }
+ ],
+ [#Rule 134
+ 'expr', 3,
+sub
+#line 360 "Parser.yp"
+{ "$_[1] $_[2] $_[3]" }
+ ],
+ [#Rule 135
+ 'expr', 3,
+sub
+#line 361 "Parser.yp"
+{ "$_[1] $_[2] $_[3]" }
+ ],
+ [#Rule 136
+ 'expr', 3,
+sub
+#line 362 "Parser.yp"
+{ "int($_[1] / $_[3])" }
+ ],
+ [#Rule 137
+ 'expr', 3,
+sub
+#line 363 "Parser.yp"
+{ "$_[1] % $_[3]" }
+ ],
+ [#Rule 138
+ 'expr', 3,
+sub
+#line 364 "Parser.yp"
+{ "$_[1] $CMPOP{ $_[2] } $_[3]" }
+ ],
+ [#Rule 139
+ 'expr', 3,
+sub
+#line 365 "Parser.yp"
+{ "$_[1] . $_[3]" }
+ ],
+ [#Rule 140
+ 'expr', 3,
+sub
+#line 366 "Parser.yp"
+{ "$_[1] && $_[3]" }
+ ],
+ [#Rule 141
+ 'expr', 3,
+sub
+#line 367 "Parser.yp"
+{ "$_[1] || $_[3]" }
+ ],
+ [#Rule 142
+ 'expr', 2,
+sub
+#line 368 "Parser.yp"
+{ "! $_[2]" }
+ ],
+ [#Rule 143
+ 'expr', 5,
+sub
+#line 369 "Parser.yp"
+{ "$_[1] ? $_[3] : $_[5]" }
+ ],
+ [#Rule 144
+ 'expr', 3,
+sub
+#line 370 "Parser.yp"
+{ $factory->assign(@{$_[2]}) }
+ ],
+ [#Rule 145
+ 'expr', 3,
+sub
+#line 371 "Parser.yp"
+{ "($_[2])" }
+ ],
+ [#Rule 146
+ 'expr', 1, undef
+ ],
+ [#Rule 147
+ 'setlist', 2,
+sub
+#line 375 "Parser.yp"
+{ push(@{$_[1]}, @{$_[2]}); $_[1] }
+ ],
+ [#Rule 148
+ 'setlist', 2, undef
+ ],
+ [#Rule 149
+ 'setlist', 1, undef
+ ],
+ [#Rule 150
+ 'assign', 3,
+sub
+#line 381 "Parser.yp"
+{ [ $_[1], $_[3] ] }
+ ],
+ [#Rule 151
+ 'assign', 3,
+sub
+#line 382 "Parser.yp"
+{ [ @_[1,3] ] }
+ ],
+ [#Rule 152
+ 'args', 2,
+sub
+#line 389 "Parser.yp"
+{ push(@{$_[1]}, $_[2]); $_[1] }
+ ],
+ [#Rule 153
+ 'args', 2,
+sub
+#line 390 "Parser.yp"
+{ push(@{$_[1]->[0]}, $_[2]); $_[1] }
+ ],
+ [#Rule 154
+ 'args', 4,
+sub
+#line 391 "Parser.yp"
+{ push(@{$_[1]->[0]}, "'', " .
+ $factory->assign(@_[2,4])); $_[1] }
+ ],
+ [#Rule 155
+ 'args', 2,
+sub
+#line 393 "Parser.yp"
+{ $_[1] }
+ ],
+ [#Rule 156
+ 'args', 0,
+sub
+#line 394 "Parser.yp"
+{ [ [ ] ] }
+ ],
+ [#Rule 157
+ 'lnameargs', 3,
+sub
+#line 404 "Parser.yp"
+{ push(@{$_[3]}, $_[1]); $_[3] }
+ ],
+ [#Rule 158
+ 'lnameargs', 1, undef
+ ],
+ [#Rule 159
+ 'lvalue', 1, undef
+ ],
+ [#Rule 160
+ 'lvalue', 3,
+sub
+#line 409 "Parser.yp"
+{ $factory->quoted($_[2]) }
+ ],
+ [#Rule 161
+ 'lvalue', 1, undef
+ ],
+ [#Rule 162
+ 'nameargs', 3,
+sub
+#line 413 "Parser.yp"
+{ [ [$factory->ident($_[2])], $_[3] ] }
+ ],
+ [#Rule 163
+ 'nameargs', 2,
+sub
+#line 414 "Parser.yp"
+{ [ @_[1,2] ] }
+ ],
+ [#Rule 164
+ 'nameargs', 4,
+sub
+#line 415 "Parser.yp"
+{ [ @_[1,3] ] }
+ ],
+ [#Rule 165
+ 'names', 3,
+sub
+#line 418 "Parser.yp"
+{ push(@{$_[1]}, $_[3]); $_[1] }
+ ],
+ [#Rule 166
+ 'names', 1,
+sub
+#line 419 "Parser.yp"
+{ [ $_[1] ] }
+ ],
+ [#Rule 167
+ 'name', 3,
+sub
+#line 422 "Parser.yp"
+{ $factory->quoted($_[2]) }
+ ],
+ [#Rule 168
+ 'name', 1,
+sub
+#line 423 "Parser.yp"
+{ "'$_[1]'" }
+ ],
+ [#Rule 169
+ 'name', 1, undef
+ ],
+ [#Rule 170
+ 'filename', 3,
+sub
+#line 435 "Parser.yp"
+{ "$_[1].$_[3]" }
+ ],
+ [#Rule 171
+ 'filename', 1, undef
+ ],
+ [#Rule 172
+ 'filepart', 1, undef
+ ],
+ [#Rule 173
+ 'filepart', 1, undef
+ ],
+ [#Rule 174
+ 'filepart', 1, undef
+ ],
+ [#Rule 175
+ 'quoted', 2,
+sub
+#line 449 "Parser.yp"
+{ push(@{$_[1]}, $_[2])
+ if defined $_[2]; $_[1] }
+ ],
+ [#Rule 176
+ 'quoted', 0,
+sub
+#line 451 "Parser.yp"
+{ [ ] }
+ ],
+ [#Rule 177
+ 'quotable', 1,
+sub
+#line 454 "Parser.yp"
+{ $factory->ident($_[1]) }
+ ],
+ [#Rule 178
+ 'quotable', 1,
+sub
+#line 455 "Parser.yp"
+{ $factory->text($_[1]) }
+ ],
+ [#Rule 179
+ 'quotable', 1,
+sub
+#line 456 "Parser.yp"
+{ undef }
+ ]
+];
+
+
+
+1;
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/lib/Template/Iterator.pm b/lib/Template/Iterator.pm
new file mode 100644
index 0000000..0063b6e
--- /dev/null
+++ b/lib/Template/Iterator.pm
@@ -0,0 +1,446 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Iterator
+#
+# DESCRIPTION
+#
+# Module defining an iterator class which is used by the FOREACH
+# directive for iterating through data sets. This may be
+# sub-classed to define more specific iterator types.
+#
+# An iterator is an object which provides a consistent way to
+# navigate through data which may have a complex underlying form.
+# This implementation uses the get_first() and get_next() methods to
+# iterate through a dataset. The get_first() method is called once
+# to perform any data initialisation and return the first value,
+# then get_next() is called repeatedly to return successive values.
+# Both these methods return a pair of values which are the data item
+# itself and a status code. The default implementation handles
+# iteration through an array (list) of elements which is passed by
+# reference to the constructor. An empty list is used if none is
+# passed. The module may be sub-classed to provide custom
+# implementations which iterate through any kind of data in any
+# manner as long as it can conforms to the get_first()/get_next()
+# interface. The object also implements the get_all() method for
+# returning all remaining elements as a list reference.
+#
+# For further information on iterators see "Design Patterns", by the
+# "Gang of Four" (Erich Gamma, Richard Helm, Ralph Johnson, John
+# Vlissides), Addision-Wesley, ISBN 0-201-63361-2.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Iterator.pm,v 2.59 2003/04/24 09:14:38 abw Exp $
+#
+#============================================================================
+
+package Template::Iterator;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $AUTOLOAD ); # AUTO?
+use base qw( Template::Base );
+use Template::Constants;
+use Template::Exception;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.59 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\@target, \%options)
+#
+# Constructor method which creates and returns a reference to a new
+# Template::Iterator object. A reference to the target data (array
+# or hash) may be passed for the object to iterate through.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $data = shift || [ ];
+ my $params = shift || { };
+
+ if (ref $data eq 'HASH') {
+ # map a hash into a list of { key => ???, value => ??? } hashes,
+ # one for each key, sorted by keys
+ $data = [ map { { key => $_, value => $data->{ $_ } } }
+ sort keys %$data ];
+ }
+ elsif (UNIVERSAL::can($data, 'as_list')) {
+ $data = $data->as_list();
+ }
+ elsif (ref $data ne 'ARRAY') {
+ # coerce any non-list data into an array reference
+ $data = [ $data ] ;
+ }
+
+ bless {
+ _DATA => $data,
+ _ERROR => '',
+ }, $class;
+}
+
+
+#========================================================================
+# ----- PUBLIC OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# get_first()
+#
+# Initialises the object for iterating through the target data set. The
+# first record is returned, if defined, along with the STATUS_OK value.
+# If there is no target data, or the data is an empty set, then undef
+# is returned with the STATUS_DONE value.
+#------------------------------------------------------------------------
+
+sub get_first {
+ my $self = shift;
+ my $data = $self->{ _DATA };
+
+ $self->{ _DATASET } = $self->{ _DATA };
+ my $size = scalar @$data;
+ my $index = 0;
+
+ return (undef, Template::Constants::STATUS_DONE) unless $size;
+
+ # initialise various counters, flags, etc.
+ @$self{ qw( SIZE MAX INDEX COUNT FIRST LAST ) }
+ = ( $size, $size - 1, $index, 1, 1, $size > 1 ? 0 : 1, undef );
+ @$self{ qw( PREV NEXT ) } = ( undef, $self->{ _DATASET }->[ $index + 1 ]);
+
+ return $self->{ _DATASET }->[ $index ];
+}
+
+
+
+#------------------------------------------------------------------------
+# get_next()
+#
+# Called repeatedly to access successive elements in the data set.
+# Should only be called after calling get_first() or a warning will
+# be raised and (undef, STATUS_DONE) returned.
+#------------------------------------------------------------------------
+
+sub get_next {
+ my $self = shift;
+ my ($max, $index) = @$self{ qw( MAX INDEX ) };
+ my $data = $self->{ _DATASET };
+
+ # warn about incorrect usage
+ unless (defined $index) {
+ my ($pack, $file, $line) = caller();
+ warn("iterator get_next() called before get_first() at $file line $line\n");
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+
+ # if there's still some data to go...
+ if ($index < $max) {
+ # update counters and flags
+ $index++;
+ @$self{ qw( INDEX COUNT FIRST LAST ) }
+ = ( $index, $index + 1, 0, $index == $max ? 1 : 0 );
+ @$self{ qw( PREV NEXT ) } = @$data[ $index - 1, $index + 1 ];
+ return $data->[ $index ]; ## RETURN ##
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+}
+
+
+#------------------------------------------------------------------------
+# get_all()
+#
+# Method which returns all remaining items in the iterator as a Perl list
+# reference. May be called at any time in the life-cycle of the iterator.
+# The get_first() method will be called automatically if necessary, and
+# then subsequent get_next() calls are made, storing each returned
+# result until the list is exhausted.
+#------------------------------------------------------------------------
+
+sub get_all {
+ my $self = shift;
+ my ($max, $index) = @$self{ qw( MAX INDEX ) };
+ my @data;
+
+ # if there's still some data to go...
+ if ($index < $max) {
+ $index++;
+ @data = @{ $self->{ _DATASET } } [ $index..$max ];
+
+ # update counters and flags
+ @$self{ qw( INDEX COUNT FIRST LAST ) }
+ = ( $max, $max + 1, 0, 1 );
+
+ return \@data; ## RETURN ##
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DONE); ## RETURN ##
+ }
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# Provides access to internal fields (e.g. size, first, last, max, etc)
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $item = $AUTOLOAD;
+ $item =~ s/.*:://;
+ return if $item eq 'DESTROY';
+
+ # alias NUMBER to COUNT for backwards compatability
+ $item = 'COUNT' if $item =~ /NUMBER/i;
+
+ return $self->{ uc $item };
+}
+
+
+#========================================================================
+# ----- PRIVATE DEBUG METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string detailing the internal state of
+# the iterator object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ join('',
+ " Data: ", $self->{ _DATA }, "\n",
+ " Index: ", $self->{ INDEX }, "\n",
+ "Number: ", $self->{ NUMBER }, "\n",
+ " Max: ", $self->{ MAX }, "\n",
+ " Size: ", $self->{ SIZE }, "\n",
+ " First: ", $self->{ FIRST }, "\n",
+ " Last: ", $self->{ LAST }, "\n",
+ "\n"
+ );
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Iterator - Data iterator used by the FOREACH directive
+
+=head1 SYNOPSIS
+
+ my $iter = Template::Iterator->new(\@data, \%options);
+
+=head1 DESCRIPTION
+
+The Template::Iterator module defines a generic data iterator for use
+by the FOREACH directive.
+
+It may be used as the base class for custom iterators.
+
+=head1 PUBLIC METHODS
+
+=head2 new($data)
+
+Constructor method. A reference to a list of values is passed as the
+first parameter. Subsequent calls to get_first() and get_next() calls
+will return each element from the list.
+
+ my $iter = Template::Iterator->new([ 'foo', 'bar', 'baz' ]);
+
+The constructor will also accept a reference to a hash array and will
+expand it into a list in which each entry is a hash array containing
+a 'key' and 'value' item, sorted according to the hash keys.
+
+ my $iter = Template::Iterator->new({
+ foo => 'Foo Item',
+ bar => 'Bar Item',
+ });
+
+This is equivalent to:
+
+ my $iter = Template::Iterator->new([
+ { key => 'bar', value => 'Bar Item' },
+ { key => 'foo', value => 'Foo Item' },
+ ]);
+
+When passed a single item which is not an array reference, the constructor
+will automatically create a list containing that single item.
+
+ my $iter = Template::Iterator->new('foo');
+
+This is equivalent to:
+
+ my $iter = Template::Iterator->new([ 'foo' ]);
+
+Note that a single item which is an object based on a blessed ARRAY
+references will NOT be treated as an array and will be folded into
+a list containing that one object reference.
+
+ my $list = bless [ 'foo', 'bar' ], 'MyListClass';
+ my $iter = Template::Iterator->new($list);
+
+equivalent to:
+
+ my $iter = Template::Iterator->new([ $list ]);
+
+If the object provides an as_list() method then the Template::Iterator
+constructor will call that method to return the list of data. For example:
+
+ package MyListObject;
+
+ sub new {
+ my $class = shift;
+ bless [ @_ ], $class;
+ }
+
+ package main;
+
+ my $list = MyListObject->new('foo', 'bar');
+ my $iter = Template::Iterator->new($list);
+
+This is then functionally equivalent to:
+
+ my $iter = Template::Iterator->new([ $list ]);
+
+The iterator will return only one item, a reference to the MyListObject
+object, $list.
+
+By adding an as_list() method to the MyListObject class, we can force
+the Template::Iterator constructor to treat the object as a list and
+use the data contained within.
+
+ package MyListObject;
+
+ ...
+
+ sub as_list {
+ my $self = shift;
+ return $self;
+ }
+
+ package main;
+
+ my $list = MyListObject->new('foo', 'bar');
+ my $iter = Template::Iterator->new($list);
+
+The iterator will now return the two item, 'foo' and 'bar', which the
+MyObjectList encapsulates.
+
+=head2 get_first()
+
+Returns a ($value, $error) pair for the first item in the iterator set.
+The $error returned may be zero or undefined to indicate a valid datum
+was successfully returned. Returns an error of STATUS_DONE if the list
+is empty.
+
+=head2 get_next()
+
+Returns a ($value, $error) pair for the next item in the iterator set.
+Returns an error of STATUS_DONE if all items in the list have been
+visited.
+
+=head2 get_all()
+
+Returns a (\@values, $error) pair for all remaining items in the iterator
+set. Returns an error of STATUS_DONE if all items in the list have been
+visited.
+
+=head2 size()
+
+Returns the size of the data set or undef if unknown.
+
+=head2 max()
+
+Returns the maximum index number (i.e. the index of the last element)
+which is equivalent to size() - 1.
+
+=head2 index()
+
+Returns the current index number which is in the range 0 to max().
+
+=head2 count()
+
+Returns the current iteration count in the range 1 to size(). This is
+equivalent to index() + 1. Note that number() is supported as an alias
+for count() for backwards compatability.
+
+=head2 first()
+
+Returns a boolean value to indicate if the iterator is currently on
+the first iteration of the set.
+
+=head2 last()
+
+Returns a boolean value to indicate if the iterator is currently on
+the last iteration of the set.
+
+=head2 prev()
+
+Returns the previous item in the data set, or undef if the iterator is
+on the first item.
+
+=head2 next()
+
+Returns the next item in the data set or undef if the iterator is on the
+last item.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.59, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>
diff --git a/lib/Template/Namespace/Constants.pm b/lib/Template/Namespace/Constants.pm
new file mode 100644
index 0000000..76e5366
--- /dev/null
+++ b/lib/Template/Namespace/Constants.pm
@@ -0,0 +1,195 @@
+#================================================================= -*-Perl-*-
+#
+# Template::Namespace::Constants
+#
+# DESCRIPTION
+# Plugin compiler module for performing constant folding at compile time
+# on variables in a particular namespace.
+#
+# AUTHOR
+# Andy Wardley <abw@andywardley.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2002 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# REVISION
+# $Id: Constants.pm,v 1.17 2003/04/24 09:14:42 abw Exp $
+#
+#============================================================================
+
+package Template::Namespace::Constants;
+
+use strict;
+use Template::Base;
+use Template::Config;
+use Template::Directive;
+use Template::Exception;
+
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.17 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+
+
+sub _init {
+ my ($self, $config) = @_;
+ $self->{ STASH } = Template::Config->stash($config)
+ || return $self->error(Template::Config->error());
+ return $self;
+}
+
+
+
+#------------------------------------------------------------------------
+# ident(\@ident) foo.bar(baz)
+#------------------------------------------------------------------------
+
+sub ident {
+ my ($self, $ident) = @_;
+ my @save = @$ident;
+
+ # discard first node indicating constants namespace
+ splice(@$ident, 0, 2);
+
+ my $nelems = @$ident / 2;
+ my ($e, $result);
+ local $" = ', ';
+
+ print STDERR "constant ident [ @$ident ] " if $DEBUG;
+
+ foreach $e (0..$nelems-1) {
+ # node name must be a constant
+ unless ($ident->[$e * 2] =~ s/^'(.+)'$/$1/s) {
+ $self->DEBUG(" * deferred (non-constant item: ", $ident->[$e * 2], ")\n")
+ if $DEBUG;
+ return Template::Directive->ident(\@save);
+ }
+
+ # if args is non-zero then it must be eval'ed
+ if ($ident->[$e * 2 + 1]) {
+ my $args = $ident->[$e * 2 + 1];
+ my $comp = eval "$args";
+ if ($@) {
+ $self->DEBUG(" * deferred (non-constant args: $args)\n") if $DEBUG;
+ return Template::Directive->ident(\@save);
+ }
+ $self->DEBUG("($args) ") if $comp && $DEBUG;
+ $ident->[$e * 2 + 1] = $comp;
+ }
+ }
+
+
+ $result = $self->{ STASH }->get($ident);
+
+ if (! length $result || ref $result) {
+ my $reason = length $result ? 'reference' : 'no result';
+ $self->DEBUG(" * deferred ($reason)\n") if $DEBUG;
+ return Template::Directive->ident(\@save);
+ }
+
+ $result =~ s/'/\\'/g;
+
+ $self->DEBUG(" * resolved => '$result'\n") if $DEBUG;
+
+ return "'$result'";
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Namespace::Constants - Compile time constant folding
+
+=head1 SYNOPSIS
+
+ # easy way to define constants
+ use Template;
+
+ my $tt = Template->new({
+ CONSTANTS => {
+ pi => 3.14,
+ e => 2.718,
+ },
+ });
+
+ # nitty-gritty, hands-dirty way
+ use Template::Namespace::Constants;
+
+ my $tt = Template->new({
+ NAMESPACE => {
+ constants => Template::Namespace::Constants->new({
+ pi => 3.14,
+ e => 2.718,
+ },
+ },
+ });
+
+=head1 DESCRIPTION
+
+The Template::Namespace::Constants module implements a namespace handler
+which is plugged into the Template::Directive compiler module. This then
+performs compile time constant folding of variables in a particular namespace.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%constants)
+
+The new() constructor method creates and returns a reference to a new
+Template::Namespace::Constants object. This creates an internal stash
+to store the constant variable definitions passed as arguments.
+
+ my $handler = Template::Namespace::Constants->new({
+ pi => 3.14,
+ e => 2.718,
+ });
+
+=head2 ident(\@ident)
+
+Method called to resolve a variable identifier into a compiled form. In this
+case, the method fetches the corresponding constant value from its internal
+stash and returns it.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+1.17, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Directive|Template::Directive>
diff --git a/lib/Template/Parser.pm b/lib/Template/Parser.pm
new file mode 100644
index 0000000..34f777d
--- /dev/null
+++ b/lib/Template/Parser.pm
@@ -0,0 +1,1434 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Parser
+#
+# DESCRIPTION
+# This module implements a LALR(1) parser and assocated support
+# methods to parse template documents into the appropriate "compiled"
+# format. Much of the parser DFA code (see _parse() method) is based
+# on Francois Desarmenien's Parse::Yapp module. Kudos to him.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# The following copyright notice appears in the Parse::Yapp
+# documentation.
+#
+# The Parse::Yapp module and its related modules and shell
+# scripts are copyright (c) 1998 Francois Desarmenien,
+# France. All rights reserved.
+#
+# You may use and distribute them under the terms of either
+# the GNU General Public License or the Artistic License, as
+# specified in the Perl README file.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Parser.pm,v 2.75 2003/07/01 12:44:56 darren Exp $
+#
+#============================================================================
+
+package Template::Parser;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $ERROR );
+use base qw( Template::Base );
+use vars qw( $TAG_STYLE $DEFAULT_STYLE $QUOTED_ESCAPES );
+
+use Template::Constants qw( :status :chomp );
+use Template::Directive;
+use Template::Grammar;
+
+# parser state constants
+use constant CONTINUE => 0;
+use constant ACCEPT => 1;
+use constant ERROR => 2;
+use constant ABORT => 3;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.75 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+$ERROR = '';
+
+
+#========================================================================
+# -- COMMON TAG STYLES --
+#========================================================================
+
+$TAG_STYLE = {
+ 'default' => [ '\[%', '%\]' ],
+ 'template1' => [ '[\[%]%', '%[\]%]' ],
+ 'metatext' => [ '%%', '%%' ],
+ 'html' => [ '<!--', '-->' ],
+ 'mason' => [ '<%', '>' ],
+ 'asp' => [ '<%', '%>' ],
+ 'php' => [ '<\?', '\?>' ],
+ 'star' => [ '\[\*', '\*\]' ],
+};
+$TAG_STYLE->{ template } = $TAG_STYLE->{ tt2 } = $TAG_STYLE->{ default };
+
+
+$DEFAULT_STYLE = {
+ START_TAG => $TAG_STYLE->{ default }->[0],
+ END_TAG => $TAG_STYLE->{ default }->[1],
+# TAG_STYLE => 'default',
+ ANYCASE => 0,
+ INTERPOLATE => 0,
+ PRE_CHOMP => 0,
+ POST_CHOMP => 0,
+ V1DOLLAR => 0,
+ EVAL_PERL => 0,
+};
+
+$QUOTED_ESCAPES = {
+ n => "\n",
+ r => "\r",
+ t => "\t",
+};
+
+
+#========================================================================
+# ----- PUBLIC METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\%config)
+#
+# Constructor method.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $config = $_[0] && UNIVERSAL::isa($_[0], 'HASH') ? shift(@_) : { @_ };
+ my ($tagstyle, $debug, $start, $end, $defaults, $grammar, $hash, $key, $udef);
+
+ my $self = bless {
+ START_TAG => undef,
+ END_TAG => undef,
+ TAG_STYLE => 'default',
+ ANYCASE => 0,
+ INTERPOLATE => 0,
+ PRE_CHOMP => 0,
+ POST_CHOMP => 0,
+ V1DOLLAR => 0,
+ EVAL_PERL => 0,
+ GRAMMAR => undef,
+ _ERROR => '',
+ FACTORY => 'Template::Directive',
+ }, $class;
+
+ # update self with any relevant keys in config
+ foreach $key (keys %$self) {
+ $self->{ $key } = $config->{ $key } if defined $config->{ $key };
+ }
+ $self->{ FILEINFO } = [ ];
+
+ # DEBUG config item can be a bitmask
+ if (defined ($debug = $config->{ DEBUG })) {
+ $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PARSER
+ | Template::Constants::DEBUG_FLAGS );
+ $self->{ DEBUG_DIRS } = $debug & Template::Constants::DEBUG_DIRS;
+ }
+ # package variable can be set to 1 to support previous behaviour
+ elsif ($DEBUG == 1) {
+ $self->{ DEBUG } = Template::Constants::DEBUG_PARSER;
+ $self->{ DEBUG_DIRS } = 0;
+ }
+ # otherwise let $DEBUG be a bitmask
+ else {
+ $self->{ DEBUG } = $DEBUG & ( Template::Constants::DEBUG_PARSER
+ | Template::Constants::DEBUG_FLAGS );
+ $self->{ DEBUG_DIRS } = $DEBUG & Template::Constants::DEBUG_DIRS;
+ }
+
+ $grammar = $self->{ GRAMMAR } ||= do {
+ require Template::Grammar;
+ Template::Grammar->new();
+ };
+
+ # build a FACTORY object to include any NAMESPACE definitions,
+ # but only if FACTORY isn't already an object
+ if ($config->{ NAMESPACE } && ! ref $self->{ FACTORY }) {
+ my $fclass = $self->{ FACTORY };
+ $self->{ FACTORY } = $fclass->new( NAMESPACE => $config->{ NAMESPACE } )
+ || return $class->error($fclass->error());
+ }
+
+
+# # determine START_TAG and END_TAG for specified (or default) TAG_STYLE
+# $tagstyle = $self->{ TAG_STYLE } || 'default';
+# return $class->error("Invalid tag style: $tagstyle")
+# unless defined ($start = $TAG_STYLE->{ $tagstyle });
+# ($start, $end) = @$start;
+#
+# $self->{ START_TAG } ||= $start;
+# $self->{ END_TAG } ||= $end;
+
+ # load grammar rules, states and lex table
+ @$self{ qw( LEXTABLE STATES RULES ) }
+ = @$grammar{ qw( LEXTABLE STATES RULES ) };
+
+ $self->new_style($config)
+ || return $class->error($self->error());
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# new_style(\%config)
+#
+# Install a new (stacked) parser style. This feature is currently
+# experimental but should mimic the previous behaviour with regard to
+# TAG_STYLE, START_TAG, END_TAG, etc.
+#------------------------------------------------------------------------
+
+sub new_style {
+ my ($self, $config) = @_;
+ my $styles = $self->{ STYLE } ||= [ ];
+ my ($tagstyle, $tags, $start, $end, $key);
+
+ # clone new style from previous or default style
+ my $style = { %{ $styles->[-1] || $DEFAULT_STYLE } };
+
+ # expand START_TAG and END_TAG from specified TAG_STYLE
+ if ($tagstyle = $config->{ TAG_STYLE }) {
+ return $self->error("Invalid tag style: $tagstyle")
+ unless defined ($tags = $TAG_STYLE->{ $tagstyle });
+ ($start, $end) = @$tags;
+ $config->{ START_TAG } ||= $start;
+ $config->{ END_TAG } ||= $end;
+ }
+
+ foreach $key (keys %$DEFAULT_STYLE) {
+ $style->{ $key } = $config->{ $key } if defined $config->{ $key };
+ }
+ push(@$styles, $style);
+ return $style;
+}
+
+
+#------------------------------------------------------------------------
+# old_style()
+#
+# Pop the current parser style and revert to the previous one. See
+# new_style(). ** experimental **
+#------------------------------------------------------------------------
+
+sub old_style {
+ my $self = shift;
+ my $styles = $self->{ STYLE };
+ return $self->error('only 1 parser style remaining')
+ unless (@$styles > 1);
+ pop @$styles;
+ return $styles->[-1];
+}
+
+
+#------------------------------------------------------------------------
+# parse($text, $data)
+#
+# Parses the text string, $text and returns a hash array representing
+# the compiled template block(s) as Perl code, in the format expected
+# by Template::Document.
+#------------------------------------------------------------------------
+
+sub parse {
+ my ($self, $text, $info) = @_;
+ my ($tokens, $block);
+
+ $info->{ DEBUG } = $self->{ DEBUG_DIRS }
+ unless defined $info->{ DEBUG };
+
+# print "info: { ", join(', ', map { "$_ => $info->{ $_ }" } keys %$info), " }\n";
+
+ # store for blocks defined in the template (see define_block())
+ my $defblock = $self->{ DEFBLOCK } = { };
+ my $metadata = $self->{ METADATA } = [ ];
+
+ $self->{ _ERROR } = '';
+
+ # split file into TEXT/DIRECTIVE chunks
+ $tokens = $self->split_text($text)
+ || return undef; ## RETURN ##
+
+ push(@{ $self->{ FILEINFO } }, $info);
+
+ # parse chunks
+ $block = $self->_parse($tokens, $info);
+
+ pop(@{ $self->{ FILEINFO } });
+
+ return undef unless $block; ## RETURN ##
+
+ $self->debug("compiled main template document block:\n$block")
+ if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER;
+
+ return {
+ BLOCK => $block,
+ DEFBLOCKS => $defblock,
+ METADATA => { @$metadata },
+ };
+}
+
+
+
+#------------------------------------------------------------------------
+# split_text($text)
+#
+# Split input template text into directives and raw text chunks.
+#------------------------------------------------------------------------
+
+sub split_text {
+ my ($self, $text) = @_;
+ my ($pre, $dir, $prelines, $dirlines, $postlines, $chomp, $tags, @tags);
+ my $style = $self->{ STYLE }->[-1];
+ my ($start, $end, $prechomp, $postchomp, $interp ) =
+ @$style{ qw( START_TAG END_TAG PRE_CHOMP POST_CHOMP INTERPOLATE ) };
+
+ my @tokens = ();
+ my $line = 1;
+
+ return \@tokens ## RETURN ##
+ unless defined $text && length $text;
+
+ # extract all directives from the text
+ while ($text =~ s/
+ ^(.*?) # $1 - start of line up to directive
+ (?:
+ $start # start of tag
+ (.*?) # $2 - tag contents
+ $end # end of tag
+ )
+ //sx) {
+
+ ($pre, $dir) = ($1, $2);
+ $pre = '' unless defined $pre;
+ $dir = '' unless defined $dir;
+
+ $postlines = 0; # denotes lines chomped
+ $prelines = ($pre =~ tr/\n//); # NULL - count only
+ $dirlines = ($dir =~ tr/\n//); # ditto
+
+ # the directive CHOMP options may modify the preceding text
+ for ($dir) {
+ # remove leading whitespace and check for a '-' chomp flag
+ s/^([-+\#])?\s*//s;
+ if ($1 && $1 eq '#') {
+ # comment out entire directive except for any chomp flag
+ $dir = ($dir =~ /([-+])$/) ? $1 : '';
+ }
+ else {
+ $chomp = ($1 && $1 eq '+') ? 0 : ($1 || $prechomp);
+# my $space = $prechomp == &Template::Constants::CHOMP_COLLAPSE
+ my $space = $prechomp == CHOMP_COLLAPSE
+ ? ' ' : '';
+
+ # chomp off whitespace and newline preceding directive
+ $chomp and $pre =~ s/(\n|^)([ \t]*)\Z/($1||$2) ? $space : ''/me
+ and $1 eq "\n"
+ and $prelines++;
+ }
+
+ # remove trailing whitespace and check for a '-' chomp flag
+ s/\s*([-+])?\s*$//s;
+ $chomp = ($1 && $1 eq '+') ? 0 : ($1 || $postchomp);
+ my $space = $postchomp == &Template::Constants::CHOMP_COLLAPSE
+ ? ' ' : '';
+
+ $postlines++
+ if $chomp and $text =~ s/
+ ^
+ ([ \t]*)\n # whitespace to newline
+ (?:(.|\n)|$) # any char (not EOF)
+ /
+ (($1||$2) ? $space : '') . (defined $2 ? $2 : '')
+ /ex;
+ }
+
+ # any text preceding the directive can now be added
+ if (length $pre) {
+ push(@tokens, $interp
+ ? [ $pre, $line, 'ITEXT' ]
+ : ('TEXT', $pre) );
+ $line += $prelines;
+ }
+
+ # and now the directive, along with line number information
+ if (length $dir) {
+ # the TAGS directive is a compile-time switch
+ if ($dir =~ /^TAGS\s+(.*)/i) {
+ my @tags = split(/\s+/, $1);
+ if (scalar @tags > 1) {
+ ($start, $end) = map { quotemeta($_) } @tags;
+ }
+ elsif ($tags = $TAG_STYLE->{ $tags[0] }) {
+ ($start, $end) = @$tags;
+ }
+ else {
+ warn "invalid TAGS style: $tags[0]\n";
+ }
+ }
+ else {
+ # DIRECTIVE is pushed as [ $dirtext, $line_no(s), \@tokens ]
+ push(@tokens, [ $dir,
+ ($dirlines
+ ? sprintf("%d-%d", $line, $line + $dirlines)
+ : $line),
+ $self->tokenise_directive($dir) ]);
+ }
+ }
+
+ # update line counter to include directive lines and any extra
+ # newline chomped off the start of the following text
+ $line += $dirlines + $postlines;
+ }
+
+ # anything remaining in the string is plain text
+ push(@tokens, $interp
+ ? [ $text, $line, 'ITEXT' ]
+ : ( 'TEXT', $text) )
+ if length $text;
+
+ return \@tokens; ## RETURN ##
+}
+
+
+
+#------------------------------------------------------------------------
+# interpolate_text($text, $line)
+#
+# Examines $text looking for any variable references embedded like
+# $this or like ${ this }.
+#------------------------------------------------------------------------
+
+sub interpolate_text {
+ my ($self, $text, $line) = @_;
+ my @tokens = ();
+ my ($pre, $var, $dir);
+
+
+ while ($text =~
+ /
+ ( (?: \\. | [^\$] ){1,3000} ) # escaped or non-'$' character [$1]
+ |
+ ( \$ (?: # embedded variable [$2]
+ (?: \{ ([^\}]*) \} ) # ${ ... } [$3]
+ |
+ ([\w\.]+) # $word [$4]
+ )
+ )
+ /gx) {
+
+ ($pre, $var, $dir) = ($1, $3 || $4, $2);
+
+ # preceding text
+ if (defined($pre) && length($pre)) {
+ $line += $pre =~ tr/\n//;
+ $pre =~ s/\\\$/\$/g;
+ push(@tokens, 'TEXT', $pre);
+ }
+ # $variable reference
+ if ($var) {
+ $line += $dir =~ tr/\n/ /;
+ push(@tokens, [ $dir, $line, $self->tokenise_directive($var) ]);
+ }
+ # other '$' reference - treated as text
+ elsif ($dir) {
+ $line += $dir =~ tr/\n//;
+ push(@tokens, 'TEXT', $dir);
+ }
+ }
+
+ return \@tokens;
+}
+
+
+
+#------------------------------------------------------------------------
+# tokenise_directive($text)
+#
+# Called by the private _parse() method when it encounters a DIRECTIVE
+# token in the list provided by the split_text() or interpolate_text()
+# methods. The directive text is passed by parameter.
+#
+# The method splits the directive into individual tokens as recognised
+# by the parser grammar (see Template::Grammar for details). It
+# constructs a list of tokens each represented by 2 elements, as per
+# split_text() et al. The first element contains the token type, the
+# second the token itself.
+#
+# The method tokenises the string using a complex (but fast) regex.
+# For a deeper understanding of the regex magic at work here, see
+# Jeffrey Friedl's excellent book "Mastering Regular Expressions",
+# from O'Reilly, ISBN 1-56592-257-3
+#
+# Returns a reference to the list of chunks (each one being 2 elements)
+# identified in the directive text. On error, the internal _ERROR string
+# is set and undef is returned.
+#------------------------------------------------------------------------
+
+sub tokenise_directive {
+ my ($self, $text, $line) = @_;
+ my ($token, $uctoken, $type, $lookup);
+ my $lextable = $self->{ LEXTABLE };
+ my $style = $self->{ STYLE }->[-1];
+ my ($anycase, $start, $end) = @$style{ qw( ANYCASE START_TAG END_TAG ) };
+ my @tokens = ( );
+
+ while ($text =~
+ /
+ # strip out any comments
+ (\#[^\n]*)
+ |
+ # a quoted phrase matches in $3
+ (["']) # $2 - opening quote, ' or "
+ ( # $3 - quoted text buffer
+ (?: # repeat group (no backreference)
+ \\\\ # an escaped backslash \\
+ | # ...or...
+ \\\2 # an escaped quote \" or \' (match $1)
+ | # ...or...
+ . # any other character
+ | \n
+ )*? # non-greedy repeat
+ ) # end of $3
+ \2 # match opening quote
+ |
+ # an unquoted number matches in $4
+ (-?\d+(?:\.\d+)?) # numbers
+ |
+ # filename matches in $5
+ ( \/?\w+(?:(?:\/|::?)\w*)+ | \/\w+)
+ |
+ # an identifier matches in $6
+ (\w+) # variable identifier
+ |
+ # an unquoted word or symbol matches in $7
+ ( [(){}\[\]:;,\/\\] # misc parenthesis and symbols
+# | \-> # arrow operator (for future?)
+ | [+\-*] # math operations
+ | \$\{? # dollar with option left brace
+ | => # like '='
+ | [=!<>]?= | [!<>] # eqality tests
+ | &&? | \|\|? # boolean ops
+ | \.\.? # n..n sequence
+ | \S+ # something unquoted
+ ) # end of $7
+ /gmxo) {
+
+ # ignore comments to EOL
+ next if $1;
+
+ # quoted string
+ if (defined ($token = $3)) {
+ # double-quoted string may include $variable references
+ if ($2 eq '"') {
+ if ($token =~ /[\$\\]/) {
+ $type = 'QUOTED';
+ # unescape " and \ but leave \$ escaped so that
+ # interpolate_text() doesn't incorrectly treat it
+ # as a variable reference
+# $token =~ s/\\([\\"])/$1/g;
+ for ($token) {
+ s/\\([^\$nrt])/$1/g;
+ s/\\([nrt])/$QUOTED_ESCAPES->{ $1 }/ge;
+ }
+ push(@tokens, ('"') x 2,
+ @{ $self->interpolate_text($token) },
+ ('"') x 2);
+ next;
+ }
+ else {
+ $type = 'LITERAL';
+ $token =~ s['][\\']g;
+ $token = "'$token'";
+ }
+ }
+ else {
+ $type = 'LITERAL';
+ $token = "'$token'";
+ }
+ }
+ # number
+ elsif (defined ($token = $4)) {
+ $type = 'NUMBER';
+ }
+ elsif (defined($token = $5)) {
+ $type = 'FILENAME';
+ }
+ elsif (defined($token = $6)) {
+ # reserved words may be in lower case unless case sensitive
+ $uctoken = $anycase ? uc $token : $token;
+ if (defined ($type = $lextable->{ $uctoken })) {
+ $token = $uctoken;
+ }
+ else {
+ $type = 'IDENT';
+ }
+ }
+ elsif (defined ($token = $7)) {
+ # reserved words may be in lower case unless case sensitive
+ $uctoken = $anycase ? uc $token : $token;
+ unless (defined ($type = $lextable->{ $uctoken })) {
+ $type = 'UNQUOTED';
+ }
+ }
+
+ push(@tokens, $type, $token);
+
+# print(STDERR " +[ $type, $token ]\n")
+# if $DEBUG;
+ }
+
+# print STDERR "tokenise directive() returning:\n [ @tokens ]\n"
+# if $DEBUG;
+
+ return \@tokens; ## RETURN ##
+}
+
+
+#------------------------------------------------------------------------
+# define_block($name, $block)
+#
+# Called by the parser 'defblock' rule when a BLOCK definition is
+# encountered in the template. The name of the block is passed in the
+# first parameter and a reference to the compiled block is passed in
+# the second. This method stores the block in the $self->{ DEFBLOCK }
+# hash which has been initialised by parse() and will later be used
+# by the same method to call the store() method on the calling cache
+# to define the block "externally".
+#------------------------------------------------------------------------
+
+sub define_block {
+ my ($self, $name, $block) = @_;
+ my $defblock = $self->{ DEFBLOCK }
+ || return undef;
+
+ $self->debug("compiled block '$name':\n$block")
+ if $self->{ DEBUG } & Template::Constants::DEBUG_PARSER;
+
+ $defblock->{ $name } = $block;
+
+ return undef;
+}
+
+sub push_defblock {
+ my $self = shift;
+ my $stack = $self->{ DEFBLOCK_STACK } ||= [];
+ push(@$stack, $self->{ DEFBLOCK } );
+ $self->{ DEFBLOCK } = { };
+}
+
+sub pop_defblock {
+ my $self = shift;
+ my $defs = $self->{ DEFBLOCK };
+ my $stack = $self->{ DEFBLOCK_STACK } || return $defs;
+ return $defs unless @$stack;
+ $self->{ DEFBLOCK } = pop @$stack;
+ return $defs;
+}
+
+
+#------------------------------------------------------------------------
+# add_metadata(\@setlist)
+#------------------------------------------------------------------------
+
+sub add_metadata {
+ my ($self, $setlist) = @_;
+ my $metadata = $self->{ METADATA }
+ || return undef;
+
+ push(@$metadata, @$setlist);
+
+ return undef;
+}
+
+
+#========================================================================
+# ----- PRIVATE METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _parse(\@tokens, \@info)
+#
+# Parses the list of input tokens passed by reference and returns a
+# Template::Directive::Block object which contains the compiled
+# representation of the template.
+#
+# This is the main parser DFA loop. See embedded comments for
+# further details.
+#
+# On error, undef is returned and the internal _ERROR field is set to
+# indicate the error. This can be retrieved by calling the error()
+# method.
+#------------------------------------------------------------------------
+
+sub _parse {
+ my ($self, $tokens, $info) = @_;
+ my ($token, $value, $text, $line, $inperl);
+ my ($state, $stateno, $status, $action, $lookup, $coderet, @codevars);
+ my ($lhs, $len, $code); # rule contents
+ my $stack = [ [ 0, undef ] ]; # DFA stack
+
+# DEBUG
+# local $" = ', ';
+
+ # retrieve internal rule and state tables
+ my ($states, $rules) = @$self{ qw( STATES RULES ) };
+
+ # call the grammar set_factory method to install emitter factory
+ $self->{ GRAMMAR }->install_factory($self->{ FACTORY });
+
+ $line = $inperl = 0;
+ $self->{ LINE } = \$line;
+ $self->{ INPERL } = \$inperl;
+
+ $status = CONTINUE;
+ my $in_string = 0;
+
+ while(1) {
+ # get state number and state
+ $stateno = $stack->[-1]->[0];
+ $state = $states->[$stateno];
+
+ # see if any lookaheads exist for the current state
+ if (exists $state->{'ACTIONS'}) {
+
+ # get next token and expand any directives (i.e. token is an
+ # array ref) onto the front of the token list
+ while (! defined $token && @$tokens) {
+ $token = shift(@$tokens);
+ if (ref $token) {
+ ($text, $line, $token) = @$token;
+ if (ref $token) {
+ if ($info->{ DEBUG } && ! $in_string) {
+ # - - - - - - - - - - - - - - - - - - - - - - - - -
+ # This is gnarly. Look away now if you're easily
+ # frightened. We're pushing parse tokens onto the
+ # pending list to simulate a DEBUG directive like so:
+ # [% DEBUG msg line='20' text='INCLUDE foo' %]
+ # - - - - - - - - - - - - - - - - - - - - - - - - -
+ my $dtext = $text;
+ $dtext =~ s[(['\\])][\\$1]g;
+ unshift(@$tokens,
+ DEBUG => 'DEBUG',
+ IDENT => 'msg',
+ IDENT => 'line',
+ ASSIGN => '=',
+ LITERAL => "'$line'",
+ IDENT => 'text',
+ ASSIGN => '=',
+ LITERAL => "'$dtext'",
+ IDENT => 'file',
+ ASSIGN => '=',
+ LITERAL => "'$info->{ name }'",
+ (';') x 2,
+ @$token,
+ (';') x 2);
+ }
+ else {
+ unshift(@$tokens, @$token, (';') x 2);
+ }
+ $token = undef; # force redo
+ }
+ elsif ($token eq 'ITEXT') {
+ if ($inperl) {
+ # don't perform interpolation in PERL blocks
+ $token = 'TEXT';
+ $value = $text;
+ }
+ else {
+ unshift(@$tokens,
+ @{ $self->interpolate_text($text, $line) });
+ $token = undef; # force redo
+ }
+ }
+ }
+ else {
+ # toggle string flag to indicate if we're crossing
+ # a string boundary
+ $in_string = ! $in_string if $token eq '"';
+ $value = shift(@$tokens);
+ }
+ };
+ # clear undefined token to avoid 'undefined variable blah blah'
+ # warnings and let the parser logic pick it up in a minute
+ $token = '' unless defined $token;
+
+ # get the next state for the current lookahead token
+ $action = defined ($lookup = $state->{'ACTIONS'}->{ $token })
+ ? $lookup
+ : defined ($lookup = $state->{'DEFAULT'})
+ ? $lookup
+ : undef;
+ }
+ else {
+ # no lookahead actions
+ $action = $state->{'DEFAULT'};
+ }
+
+ # ERROR: no ACTION
+ last unless defined $action;
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # shift (+ive ACTION)
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ if ($action > 0) {
+ push(@$stack, [ $action, $value ]);
+ $token = $value = undef;
+ redo;
+ };
+
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ # reduce (-ive ACTION)
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ ($lhs, $len, $code) = @{ $rules->[ -$action ] };
+
+ # no action imples ACCEPTance
+ $action
+ or $status = ACCEPT;
+
+ # use dummy sub if code ref doesn't exist
+ $code = sub { $_[1] }
+ unless $code;
+
+ @codevars = $len
+ ? map { $_->[1] } @$stack[ -$len .. -1 ]
+ : ();
+
+ eval {
+ $coderet = &$code( $self, @codevars );
+ };
+ if ($@) {
+ my $err = $@;
+ chomp $err;
+ return $self->_parse_error($err);
+ }
+
+ # reduce stack by $len
+ splice(@$stack, -$len, $len);
+
+ # ACCEPT
+ return $coderet ## RETURN ##
+ if $status == ACCEPT;
+
+ # ABORT
+ return undef ## RETURN ##
+ if $status == ABORT;
+
+ # ERROR
+ last
+ if $status == ERROR;
+ }
+ continue {
+ push(@$stack, [ $states->[ $stack->[-1][0] ]->{'GOTOS'}->{ $lhs },
+ $coderet ]),
+ }
+
+ # ERROR ## RETURN ##
+ return $self->_parse_error('unexpected end of input')
+ unless defined $value;
+
+ # munge text of last directive to make it readable
+# $text =~ s/\n/\\n/g;
+
+ return $self->_parse_error("unexpected end of directive", $text)
+ if $value eq ';'; # end of directive SEPARATOR
+
+ return $self->_parse_error("unexpected token ($value)", $text);
+}
+
+
+
+#------------------------------------------------------------------------
+# _parse_error($msg, $dirtext)
+#
+# Method used to handle errors encountered during the parse process
+# in the _parse() method.
+#------------------------------------------------------------------------
+
+sub _parse_error {
+ my ($self, $msg, $text) = @_;
+ my $line = $self->{ LINE };
+ $line = ref($line) ? $$line : $line;
+ $line = 'unknown' unless $line;
+
+ $msg .= "\n [% $text %]"
+ if defined $text;
+
+ return $self->error("line $line: $msg");
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method returns a string representing the internal state of the
+# object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $output = "[Template::Parser] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ foreach $key (qw( START_TAG END_TAG TAG_STYLE ANYCASE INTERPOLATE
+ PRE_CHOMP POST_CHOMP V1DOLLAR )) {
+ my $val = $self->{ $key };
+ $val = '<undef>' unless defined $val;
+ $output .= sprintf($format, $key, $val);
+ }
+
+ $output .= '}';
+ return $output;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Parser - LALR(1) parser for compiling template documents
+
+=head1 SYNOPSIS
+
+ use Template::Parser;
+
+ $parser = Template::Parser->new(\%config);
+ $template = $parser->parse($text)
+ || die $parser->error(), "\n";
+
+=head1 DESCRIPTION
+
+The Template::Parser module implements a LALR(1) parser and associated methods
+for parsing template documents into Perl code.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%params)
+
+The new() constructor creates and returns a reference to a new
+Template::Parser object. A reference to a hash may be supplied as a
+parameter to provide configuration values. These may include:
+
+=over
+
+
+
+
+=item START_TAG, END_TAG
+
+The START_TAG and END_TAG options are used to specify character
+sequences or regular expressions that mark the start and end of a
+template directive. The default values for START_TAG and END_TAG are
+'[%' and '%]' respectively, giving us the familiar directive style:
+
+ [% example %]
+
+Any Perl regex characters can be used and therefore should be escaped
+(or use the Perl C<quotemeta> function) if they are intended to
+represent literal characters.
+
+ my $parser = Template::Parser->new({
+ START_TAG => quotemeta('<+'),
+ END_TAG => quotemeta('+>'),
+ });
+
+example:
+
+ <+ INCLUDE foobar +>
+
+The TAGS directive can also be used to set the START_TAG and END_TAG values
+on a per-template file basis.
+
+ [% TAGS <+ +> %]
+
+
+
+
+
+
+=item TAG_STYLE
+
+The TAG_STYLE option can be used to set both START_TAG and END_TAG
+according to pre-defined tag styles.
+
+ my $parser = Template::Parser->new({
+ TAG_STYLE => 'star',
+ });
+
+Available styles are:
+
+ template [% ... %] (default)
+ template1 [% ... %] or %% ... %% (TT version 1)
+ metatext %% ... %% (Text::MetaText)
+ star [* ... *] (TT alternate)
+ php <? ... ?> (PHP)
+ asp <% ... %> (ASP)
+ mason <% ... > (HTML::Mason)
+ html <!-- ... --> (HTML comments)
+
+Any values specified for START_TAG and/or END_TAG will over-ride
+those defined by a TAG_STYLE.
+
+The TAGS directive may also be used to set a TAG_STYLE
+
+ [% TAGS html %]
+ <!-- INCLUDE header -->
+
+
+
+
+
+
+=item PRE_CHOMP, POST_CHOMP
+
+Anything outside a directive tag is considered plain text and is
+generally passed through unaltered (but see the INTERPOLATE option).
+This includes all whitespace and newlines characters surrounding
+directive tags. Directives that don't generate any output will leave
+gaps in the output document.
+
+Example:
+
+ Foo
+ [% a = 10 %]
+ Bar
+
+Output:
+
+ Foo
+
+ Bar
+
+The PRE_CHOMP and POST_CHOMP options can help to clean up some of this
+extraneous whitespace. Both are disabled by default.
+
+ my $parser = Template::Parser->new({
+ PRE_CHOMP => 1,
+ POST_CHOMP => 1,
+ });
+
+With PRE_CHOMP set to 1, the newline and whitespace preceding a directive
+at the start of a line will be deleted. This has the effect of
+concatenating a line that starts with a directive onto the end of the
+previous line.
+
+ Foo <----------.
+ |
+ ,---(PRE_CHOMP)----'
+ |
+ `-- [% a = 10 %] --.
+ |
+ ,---(POST_CHOMP)---'
+ |
+ `-> Bar
+
+With POST_CHOMP set to 1, any whitespace after a directive up to and
+including the newline will be deleted. This has the effect of joining
+a line that ends with a directive onto the start of the next line.
+
+If PRE_CHOMP or POST_CHOMP is set to 2, then instead of removing all
+the whitespace, the whitespace will be collapsed to a single space.
+This is useful for HTML, where (usually) a contiguous block of
+whitespace is rendered the same as a single space.
+
+You may use the CHOMP_NONE, CHOMP_ALL, and CHOMP_COLLAPSE constants
+from the Template::Constants module to deactivate chomping, remove
+all whitespace, or collapse whitespace to a single space.
+
+PRE_CHOMP and POST_CHOMP can be activated for individual directives by
+placing a '-' immediately at the start and/or end of the directive.
+
+ [% FOREACH user = userlist %]
+ [%- user -%]
+ [% END %]
+
+The '-' characters activate both PRE_CHOMP and POST_CHOMP for the one
+directive '[%- name -%]'. Thus, the template will be processed as if
+written:
+
+ [% FOREACH user = userlist %][% user %][% END %]
+
+Note that this is the same as if PRE_CHOMP and POST_CHOMP were set
+to CHOMP_ALL; the only way to get the CHOMP_COLLAPSE behavior is
+to set PRE_CHOMP or POST_CHOMP accordingly. If PRE_CHOMP or POST_CHOMP
+is already set to CHOMP_COLLAPSE, using '-' will give you CHOMP_COLLAPSE
+behavior, not CHOMP_ALL behavior.
+
+Similarly, '+' characters can be used to disable PRE_CHOMP or
+POST_CHOMP (i.e. leave the whitespace/newline intact) options on a
+per-directive basis.
+
+ [% FOREACH user = userlist %]
+ User: [% user +%]
+ [% END %]
+
+With POST_CHOMP enabled, the above example would be parsed as if written:
+
+ [% FOREACH user = userlist %]User: [% user %]
+ [% END %]
+
+
+
+
+
+=item INTERPOLATE
+
+The INTERPOLATE flag, when set to any true value will cause variable
+references in plain text (i.e. not surrounded by START_TAG and END_TAG)
+to be recognised and interpolated accordingly.
+
+ my $parser = Template::Parser->new({
+ INTERPOLATE => 1,
+ });
+
+Variables should be prefixed by a '$' to identify them. Curly braces
+can be used in the familiar Perl/shell style to explicitly scope the
+variable name where required.
+
+ # INTERPOLATE => 0
+ <a href="http://[% server %]/[% help %]">
+ <img src="[% images %]/help.gif"></a>
+ [% myorg.name %]
+
+ # INTERPOLATE => 1
+ <a href="http://$server/$help">
+ <img src="$images/help.gif"></a>
+ $myorg.name
+
+ # explicit scoping with { }
+ <img src="$images/${icon.next}.gif">
+
+Note that a limitation in Perl's regex engine restricts the maximum length
+of an interpolated template to around 32 kilobytes or possibly less. Files
+that exceed this limit in size will typically cause Perl to dump core with
+a segmentation fault. If you routinely process templates of this size
+then you should disable INTERPOLATE or split the templates in several
+smaller files or blocks which can then be joined backed together via
+PROCESS or INCLUDE.
+
+
+
+
+
+
+
+=item ANYCASE
+
+By default, directive keywords should be expressed in UPPER CASE. The
+ANYCASE option can be set to allow directive keywords to be specified
+in any case.
+
+ # ANYCASE => 0 (default)
+ [% INCLUDE foobar %] # OK
+ [% include foobar %] # ERROR
+ [% include = 10 %] # OK, 'include' is a variable
+
+ # ANYCASE => 1
+ [% INCLUDE foobar %] # OK
+ [% include foobar %] # OK
+ [% include = 10 %] # ERROR, 'include' is reserved word
+
+One side-effect of enabling ANYCASE is that you cannot use a variable
+of the same name as a reserved word, regardless of case. The reserved
+words are currently:
+
+ GET CALL SET DEFAULT INSERT INCLUDE PROCESS WRAPPER
+ IF UNLESS ELSE ELSIF FOR FOREACH WHILE SWITCH CASE
+ USE PLUGIN FILTER MACRO PERL RAWPERL BLOCK META
+ TRY THROW CATCH FINAL NEXT LAST BREAK RETURN STOP
+ CLEAR TO STEP AND OR NOT MOD DIV END
+
+
+The only lower case reserved words that cannot be used for variables,
+regardless of the ANYCASE option, are the operators:
+
+ and or not mod div
+
+
+
+
+
+
+
+
+=item V1DOLLAR
+
+In version 1 of the Template Toolkit, an optional leading '$' could be placed
+on any template variable and would be silently ignored.
+
+ # VERSION 1
+ [% $foo %] === [% foo %]
+ [% $hash.$key %] === [% hash.key %]
+
+To interpolate a variable value the '${' ... '}' construct was used.
+Typically, one would do this to index into a hash array when the key
+value was stored in a variable.
+
+example:
+
+ my $vars = {
+ users => {
+ aba => { name => 'Alan Aardvark', ... },
+ abw => { name => 'Andy Wardley', ... },
+ ...
+ },
+ uid => 'aba',
+ ...
+ };
+
+ $template->process('user/home.html', $vars)
+ || die $template->error(), "\n";
+
+'user/home.html':
+
+ [% user = users.${uid} %] # users.aba
+ Name: [% user.name %] # Alan Aardvark
+
+This was inconsistent with double quoted strings and also the
+INTERPOLATE mode, where a leading '$' in text was enough to indicate a
+variable for interpolation, and the additional curly braces were used
+to delimit variable names where necessary. Note that this use is
+consistent with UNIX and Perl conventions, among others.
+
+ # double quoted string interpolation
+ [% name = "$title ${user.name}" %]
+
+ # INTERPOLATE = 1
+ <img src="$images/help.gif"></a>
+ <img src="$images/${icon.next}.gif">
+
+For version 2, these inconsistencies have been removed and the syntax
+clarified. A leading '$' on a variable is now used exclusively to
+indicate that the variable name should be interpolated
+(e.g. subsituted for its value) before being used. The earlier example
+from version 1:
+
+ # VERSION 1
+ [% user = users.${uid} %]
+ Name: [% user.name %]
+
+can now be simplified in version 2 as:
+
+ # VERSION 2
+ [% user = users.$uid %]
+ Name: [% user.name %]
+
+The leading dollar is no longer ignored and has the same effect of
+interpolation as '${' ... '}' in version 1. The curly braces may
+still be used to explicitly scope the interpolated variable name
+where necessary.
+
+e.g.
+
+ [% user = users.${me.id} %]
+ Name: [% user.name %]
+
+The rule applies for all variables, both within directives and in
+plain text if processed with the INTERPOLATE option. This means that
+you should no longer (if you ever did) add a leading '$' to a variable
+inside a directive, unless you explicitly want it to be interpolated.
+
+One obvious side-effect is that any version 1 templates with variables
+using a leading '$' will no longer be processed as expected. Given
+the following variable definitions,
+
+ [% foo = 'bar'
+ bar = 'baz'
+ %]
+
+version 1 would interpret the following as:
+
+ # VERSION 1
+ [% $foo %] => [% GET foo %] => bar
+
+whereas version 2 interprets it as:
+
+ # VERSION 2
+ [% $foo %] => [% GET $foo %] => [% GET bar %] => baz
+
+In version 1, the '$' is ignored and the value for the variable 'foo' is
+retrieved and printed. In version 2, the variable '$foo' is first interpolated
+to give the variable name 'bar' whose value is then retrieved and printed.
+
+The use of the optional '$' has never been strongly recommended, but
+to assist in backwards compatibility with any version 1 templates that
+may rely on this "feature", the V1DOLLAR option can be set to 1
+(default: 0) to revert the behaviour and have leading '$' characters
+ignored.
+
+ my $parser = Template::Parser->new({
+ V1DOLLAR => 1,
+ });
+
+
+
+
+
+
+=item GRAMMAR
+
+The GRAMMAR configuration item can be used to specify an alternate
+grammar for the parser. This allows a modified or entirely new
+template language to be constructed and used by the Template Toolkit.
+
+Source templates are compiled to Perl code by the Template::Parser
+using the Template::Grammar (by default) to define the language
+structure and semantics. Compiled templates are thus inherently
+"compatible" with each other and there is nothing to prevent any
+number of different template languages being compiled and used within
+the same Template Toolkit processing environment (other than the usual
+time and memory constraints).
+
+The Template::Grammar file is constructed from a YACC like grammar
+(using Parse::YAPP) and a skeleton module template. These files are
+provided, along with a small script to rebuild the grammar, in the
+'parser' sub-directory of the distribution. You don't have to know or
+worry about these unless you want to hack on the template language or
+define your own variant. There is a README file in the same directory
+which provides some small guidance but it is assumed that you know
+what you're doing if you venture herein. If you grok LALR parsers,
+then you should find it comfortably familiar.
+
+By default, an instance of the default Template::Grammar will be
+created and used automatically if a GRAMMAR item isn't specified.
+
+ use MyOrg::Template::Grammar;
+
+ my $parser = Template::Parser->new({
+ GRAMMAR = MyOrg::Template::Grammar->new();
+ });
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable various debugging features
+of the Template::Parser module.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_PARSER | DEBUG_DIRS,
+ });
+
+The DEBUG value can include any of the following. Multiple values
+should be combined using the logical OR operator, '|'.
+
+=over 4
+
+=item DEBUG_PARSER
+
+This flag causes the L<Template::Parser|Template::Parser> to generate
+debugging messages that show the Perl code generated by parsing and
+compiling each template.
+
+=item DEBUG_DIRS
+
+This option causes the Template Toolkit to generate comments
+indicating the source file, line and original text of each directive
+in the template. These comments are embedded in the template output
+using the format defined in the DEBUG_FORMAT configuration item, or a
+simple default format if unspecified.
+
+For example, the following template fragment:
+
+
+ Hello World
+
+would generate this output:
+
+ ## input text line 1 : ##
+ Hello
+ ## input text line 2 : World ##
+ World
+
+
+=back
+
+
+
+
+=back
+
+=head2 parse($text)
+
+The parse() method parses the text passed in the first parameter and
+returns a reference to a Template::Document object which contains the
+compiled representation of the template text. On error, undef is
+returned.
+
+Example:
+
+ $doc = $parser->parse($text)
+ || die $parser->error();
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+
+
+=head1 VERSION
+
+2.75, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+The original Template::Parser module was derived from a standalone
+parser generated by version 0.16 of the Parse::Yapp module. The
+following copyright notice appears in the Parse::Yapp documentation.
+
+ The Parse::Yapp module and its related modules and shell
+ scripts are copyright (c) 1998 Francois Desarmenien,
+ France. All rights reserved.
+
+ You may use and distribute them under the terms of either
+ the GNU General Public License or the Artistic License, as
+ specified in the Perl README file.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Grammar|Template::Grammar>, L<Template::Directive|Template::Directive>
+
diff --git a/lib/Template/Plugin.pm b/lib/Template/Plugin.pm
new file mode 100644
index 0000000..664ac96
--- /dev/null
+++ b/lib/Template/Plugin.pm
@@ -0,0 +1,399 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin
+#
+# DESCRIPTION
+#
+# Module defining a base class for a plugin object which can be loaded
+# and instantiated via the USE directive.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Plugin.pm,v 2.60 2003/04/24 09:14:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin;
+
+require 5.004;
+
+use strict;
+use Template::Base;
+
+use vars qw( $VERSION $DEBUG $ERROR $AUTOLOAD );
+use base qw( Template::Base );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.60 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0;
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# load()
+#
+# Class method called when the plugin module is first loaded. It
+# returns the name of a class (by default, its own class) or a prototype
+# object which will be used to instantiate new objects. The new()
+# method is then called against the class name (class method) or
+# prototype object (object method) to create a new instances of the
+# object.
+#------------------------------------------------------------------------
+
+sub load {
+ return $_[0];
+}
+
+
+#------------------------------------------------------------------------
+# new($context, $delegate, @params)
+#
+# Object constructor which is called by the Template::Context to
+# instantiate a new Plugin object. This base class constructor is
+# used as a general mechanism to load and delegate to other Perl
+# modules. The context is passed as the first parameter, followed by
+# a reference to a delegate object or the name of the module which
+# should be loaded and instantiated. Any additional parameters passed
+# to the USE directive are forwarded to the new() constructor.
+#
+# A plugin object is returned which has an AUTOLOAD method to delegate
+# requests to the underlying object.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ bless {
+ }, $class;
+}
+
+sub old_new {
+ my ($class, $context, $delclass, @params) = @_;
+ my ($delegate, $delmod);
+
+ return $class->error("no context passed to $class constructor\n")
+ unless defined $context;
+
+ if (ref $delclass) {
+ # $delclass contains a reference to a delegate object
+ $delegate = $delclass;
+ }
+ else {
+ # delclass is the name of a module to load and instantiate
+ ($delmod = $delclass) =~ s|::|/|g;
+
+ eval {
+ require "$delmod.pm";
+ $delegate = $delclass->new(@params)
+ || die "failed to instantiate $delclass object\n";
+ };
+ return $class->error($@) if $@;
+ }
+
+ bless {
+ _CONTEXT => $context,
+ _DELEGATE => $delegate,
+ _PARAMS => \@params,
+ }, $class;
+}
+
+
+#------------------------------------------------------------------------
+# fail($error)
+#
+# Version 1 error reporting function, now replaced by error() inherited
+# from Template::Base. Raises a "deprecated function" warning and then
+# calls error().
+#------------------------------------------------------------------------
+
+sub fail {
+ my $class = shift;
+ my ($pkg, $file, $line) = caller();
+ warn "Template::Plugin::fail() is deprecated at $file line $line. Please use error()\n";
+ $class->error(@_);
+}
+
+
+#========================================================================
+# ----- OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+# General catch-all method which delegates all calls to the _DELEGATE
+# object.
+#------------------------------------------------------------------------
+
+sub OLD_AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ if (ref $self eq 'HASH') {
+ my $delegate = $self->{ _DELEGATE } || return;
+ return $delegate->$method(@_);
+ }
+ my ($pkg, $file, $line) = caller();
+# warn "no such '$method' method called on $self at $file line $line\n";
+ return undef;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin - Base class for Template Toolkit plugins
+
+=head1 SYNOPSIS
+
+ package MyOrg::Template::Plugin::MyPlugin;
+ use base qw( Template::Plugin );
+ use Template::Plugin;
+ use MyModule;
+
+ sub new {
+ my $class = shift;
+ my $context = shift;
+ bless {
+ ...
+ }, $class;
+ }
+
+=head1 DESCRIPTION
+
+A "plugin" for the Template Toolkit is simply a Perl module which
+exists in a known package location (e.g. Template::Plugin::*) and
+conforms to a regular standard, allowing it to be loaded and used
+automatically.
+
+The Template::Plugin module defines a base class from which other
+plugin modules can be derived. A plugin does not have to be derived
+from Template::Plugin but should at least conform to its object-oriented
+interface.
+
+It is recommended that you create plugins in your own package namespace
+to avoid conflict with toolkit plugins. e.g.
+
+ package MyOrg::Template::Plugin::FooBar;
+
+Use the PLUGIN_BASE option to specify the namespace that you use. e.g.
+
+ use Template;
+ my $template = Template->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugin',
+ });
+
+=head1 PLUGIN API
+
+The following methods form the basic interface between the Template
+Toolkit and plugin modules.
+
+=over 4
+
+=item load($context)
+
+This method is called by the Template Toolkit when the plugin module
+is first loaded. It is called as a package method and thus implicitly
+receives the package name as the first parameter. A reference to the
+Template::Context object loading the plugin is also passed. The
+default behaviour for the load() method is to simply return the class
+name. The calling context then uses this class name to call the new()
+package method.
+
+ package MyPlugin;
+
+ sub load { # called as MyPlugin->load($context)
+ my ($class, $context) = @_;
+ return $class; # returns 'MyPlugin'
+ }
+
+=item new($context, @params)
+
+This method is called to instantiate a new plugin object for the USE
+directive. It is called as a package method against the class name
+returned by load(). A reference to the Template::Context object creating
+the plugin is passed, along with any additional parameters specified in
+the USE directive.
+
+ sub new { # called as MyPlugin->new($context)
+ my ($class, $context, @params) = @_;
+ bless {
+ _CONTEXT => $context,
+ }, $class; # returns blessed MyPlugin object
+ }
+
+=item error($error)
+
+This method, inherited from the Template::Base module, is used for
+reporting and returning errors. It can be called as a package method
+to set/return the $ERROR package variable, or as an object method to
+set/return the object _ERROR member. When called with an argument, it
+sets the relevant variable and returns undef. When called without an
+argument, it returns the value of the variable.
+
+ sub new {
+ my ($class, $context, $dsn) = @_;
+
+ return $class->error('No data source specified')
+ unless $dsn;
+
+ bless {
+ _DSN => $dsn,
+ }, $class;
+ }
+
+ ...
+
+ my $something = MyModule->new()
+ || die MyModule->error(), "\n";
+
+ $something->do_something()
+ || die $something->error(), "\n";
+
+=back
+
+=head1 DEEPER MAGIC
+
+The Template::Context object that handles the loading and use of
+plugins calls the new() and error() methods against the package name
+returned by the load() method. In pseudo-code terms, it might look
+something like this:
+
+ $class = MyPlugin->load($context); # returns 'MyPlugin'
+
+ $object = $class->new($context, @params) # MyPlugin->new(...)
+ || die $class->error(); # MyPlugin->error()
+
+The load() method may alterately return a blessed reference to an
+object instance. In this case, new() and error() are then called as
+I<object> methods against that prototype instance.
+
+ package YourPlugin;
+
+ sub load {
+ my ($class, $context) = @_;
+ bless {
+ _CONTEXT => $context,
+ }, $class;
+ }
+
+ sub new {
+ my ($self, $context, @params) = @_;
+ return $self;
+ }
+
+In this example, we have implemented a 'Singleton' plugin. One object
+gets created when load() is called and this simply returns itself for
+each call to new().
+
+Another implementation might require individual objects to be created
+for every call to new(), but with each object sharing a reference to
+some other object to maintain cached data, database handles, etc.
+This pseudo-code example demonstrates the principle.
+
+ package MyServer;
+
+ sub load {
+ my ($class, $context) = @_;
+ bless {
+ _CONTEXT => $context,
+ _CACHE => { },
+ }, $class;
+ }
+
+ sub new {
+ my ($self, $context, @params) = @_;
+ MyClient->new($self, @params);
+ }
+
+ sub add_to_cache { ... }
+
+ sub get_from_cache { ... }
+
+
+ package MyClient;
+
+ sub new {
+ my ($class, $server, $blah) = @_;
+ bless {
+ _SERVER => $server,
+ _BLAH => $blah,
+ }, $class;
+ }
+
+ sub get {
+ my $self = shift;
+ $self->{ _SERVER }->get_from_cache(@_);
+ }
+
+ sub put {
+ my $self = shift;
+ $self->{ _SERVER }->add_to_cache(@_);
+ }
+
+When the plugin is loaded, a MyServer instance is created. The new()
+method is called against this object which instantiates and returns a
+MyClient object, primed to communicate with the creating MyServer.
+
+=head1 Template::Plugin Delegation
+
+As of version 2.01, the Template::Plugin module no longer provides an
+AUTOLOAD method to delegate to other objects or classes. This was a
+badly designed feature that caused more trouble than good. You can
+easily add your own AUTOLOAD method to perform delegation if you
+require this kind of functionality.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.60, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Plugins|Template::Plugins>, L<Template::Context|Template::Context>
diff --git a/lib/Template/Plugin/Date.pm b/lib/Template/Plugin/Date.pm
new file mode 100644
index 0000000..1cd0a60
--- /dev/null
+++ b/lib/Template/Plugin/Date.pm
@@ -0,0 +1,361 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugin::Date
+#
+# DESCRIPTION
+#
+# Plugin to generate formatted date strings.
+#
+# AUTHORS
+# Thierry-Michel Barral <kktos@electron-libre.com>
+# Andy Wardley <abw@cre.canon.co.uk>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Thierry-Michel Barral, Andy Wardley.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Date.pm,v 2.66 2003/04/24 09:14:43 abw Exp $
+#
+#============================================================================
+
+package Template::Plugin::Date;
+
+use strict;
+use vars qw( $VERSION $FORMAT @LOCALE_SUFFIX );
+use base qw( Template::Plugin );
+use Template::Plugin;
+
+use POSIX ();
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.66 $ =~ /(\d+)\.(\d+)/);
+$FORMAT = '%H:%M:%S %d-%b-%Y'; # default strftime() format
+@LOCALE_SUFFIX = qw( .ISO8859-1 .ISO_8859-15 .US-ASCII .UTF-8 );
+
+#------------------------------------------------------------------------
+# new(\%options)
+#------------------------------------------------------------------------
+
+sub new {
+ my ($class, $context, $params) = @_;
+ bless {
+ $params ? %$params : ()
+ }, $class;
+}
+
+
+#------------------------------------------------------------------------
+# now()
+#
+# Call time() to return the current system time in seconds since the epoch.
+#------------------------------------------------------------------------
+
+sub now {
+ return time();
+}
+
+
+#------------------------------------------------------------------------
+# format()
+# format($time)
+# format($time, $format)
+# format($time, $format, $locale)
+# format($time, $format, $locale, $gmt_flag)
+# format(\%named_params);
+#
+# Returns a formatted time/date string for the specified time, $time,
+# (or the current system time if unspecified) using the $format, $locale,
+# and $gmt values specified as arguments or internal values set defined
+# at construction time). Specifying a Perl-true value for $gmt will
+# override the local time zone and force the output to be for GMT.
+# Any or all of the arguments may be specified as named parameters which
+# get passed as a hash array reference as the final argument.
+# ------------------------------------------------------------------------
+
+sub format {
+ my $self = shift;
+ my $params = ref($_[$#_]) eq 'HASH' ? pop(@_) : { };
+ my $time = shift(@_) || $params->{ time } || $self->{ time }
+ || $self->now();
+ my $format = @_ ? shift(@_)
+ : ($params->{ format } || $self->{ format } || $FORMAT);
+ my $locale = @_ ? shift(@_)
+ : ($params->{ locale } || $self->{ locale });
+ my $gmt = @_ ? shift(@_)
+ : ($params->{ gmt } || $self->{ gmt });
+ my (@date, $datestr);
+
+ if ($time =~ /^\d+$/) {
+ # $time is now in seconds since epoch
+ if ($gmt) {
+ @date = (gmtime($time))[0..6];
+ }
+ else {
+ @date = (localtime($time))[0..6];
+ }
+ }
+ else {
+ # if $time is numeric, then we assume it's seconds since the epoch
+ # otherwise, we try to parse it as a 'H:M:S D:M:Y' string
+ @date = (split(/(?:\/| |:|-)/, $time))[2,1,0,3..5];
+ return (undef, Template::Exception->new('date',
+ "bad time/date string: expects 'h:m:s d:m:y' got: '$time'"))
+ unless @date >= 6 && defined $date[5];
+ $date[4] -= 1; # correct month number 1-12 to range 0-11
+ $date[5] -= 1900; # convert absolute year to years since 1900
+ $time = &POSIX::mktime(@date);
+ }
+
+ if ($locale) {
+ # format the date in a specific locale, saving and subsequently
+ # restoring the current locale.
+ my $old_locale = &POSIX::setlocale(&POSIX::LC_ALL);
+
+ # some systems expect locales to have a particular suffix
+ for my $suffix ('', @LOCALE_SUFFIX) {
+ my $try_locale = $locale.$suffix;
+ my $setlocale = &POSIX::setlocale(&POSIX::LC_ALL, $try_locale);
+ if (defined $setlocale && $try_locale eq $setlocale) {
+ $locale = $try_locale;
+ last;
+ }
+ }
+ $datestr = &POSIX::strftime($format, @date);
+ &POSIX::setlocale(&POSIX::LC_ALL, $old_locale);
+ }
+ else {
+ $datestr = &POSIX::strftime($format, @date);
+ }
+
+ return $datestr;
+}
+
+sub calc {
+ my $self = shift;
+ eval { require "Date/Calc.pm" };
+ $self->throw("failed to load Date::Calc: $@") if $@;
+ return Template::Plugin::Date::Calc->new('no context');
+}
+
+sub manip {
+ my $self = shift;
+ eval { require "Date/Manip.pm" };
+ $self->throw("failed to load Date::Manip: $@") if $@;
+ return Template::Plugin::Date::Manip->new('no context');
+}
+
+
+sub throw {
+ my $self = shift;
+ die (Template::Exception->new('date', join(', ', @_)));
+}
+
+
+package Template::Plugin::Date::Calc;
+use base qw( Template::Plugin );
+use vars qw( $AUTOLOAD );
+*throw = \&Template::Plugin::Date::throw;
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ my $sub = \&{"Date::Calc::$method"};
+ $self->throw("no such Date::Calc method: $method")
+ unless $sub;
+
+ &$sub(@_);
+}
+
+package Template::Plugin::Date::Manip;
+use base qw( Template::Plugin );
+use vars qw( $AUTOLOAD );
+*throw = \&Template::Plugin::Date::throw;
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $method = $AUTOLOAD;
+
+ $method =~ s/.*:://;
+ return if $method eq 'DESTROY';
+
+ my $sub = \&{"Date::Manip::$method"};
+ $self->throw("no such Date::Manip method: $method")
+ unless $sub;
+
+ &$sub(@_);
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugin::Date - Plugin to generate formatted date strings
+
+=head1 SYNOPSIS
+
+ [% USE date %]
+
+ # use current time and default format
+ [% date.format %]
+
+ # specify time as seconds since epoch or 'h:m:s d-m-y' string
+ [% date.format(960973980) %]
+ [% date.format('4:20:36 21/12/2000') %]
+
+ # specify format
+ [% date.format(mytime, '%H:%M:%S') %]
+
+ # specify locale
+ [% date.format(date.now, '%a %d %b %y', 'en_GB') %]
+
+ # named parameters
+ [% date.format(mytime, format = '%H:%M:%S') %]
+ [% date.format(locale = 'en_GB') %]
+ [% date.format(time = date.now,
+ format = '%H:%M:%S',
+ locale = 'en_GB) %]
+
+ # specify default format to plugin
+ [% USE date(format = '%H:%M:%S', locale = 'de_DE') %]
+
+ [% date.format %]
+ ...
+
+=head1 DESCRIPTION
+
+The Date plugin provides an easy way to generate formatted time and date
+strings by delegating to the POSIX strftime() routine.
+
+The plugin can be loaded via the familiar USE directive.
+
+ [% USE date %]
+
+This creates a plugin object with the default name of 'date'. An alternate
+name can be specified as such:
+
+ [% USE myname = date %]
+
+The plugin provides the format() method which accepts a time value, a
+format string and a locale name. All of these parameters are optional
+with the current system time, default format ('%H:%M:%S %d-%b-%Y') and
+current locale being used respectively, if undefined. Default values
+for the time, format and/or locale may be specified as named parameters
+in the USE directive.
+
+ [% USE date(format = '%a %d-%b-%Y', locale = 'fr_FR') %]
+
+When called without any parameters, the format() method returns a string
+representing the current system time, formatted by strftime() according
+to the default format and for the default locale (which may not be the
+current one, if locale is set in the USE directive).
+
+ [% date.format %]
+
+The plugin allows a time/date to be specified as seconds since the epoch,
+as is returned by time().
+
+ File last modified: [% date.format(filemod_time) %]
+
+The time/date can also be specified as a string of the form 'h:m:s d/m/y'.
+Any of the characters : / - or space may be used to delimit fields.
+
+ [% USE day = date(format => '%A', locale => 'en_GB') %]
+ [% day.format('4:20:00 9-13-2000') %]
+
+Output:
+
+ Tuesday
+
+A format string can also be passed to the format() method, and a locale
+specification may follow that.
+
+ [% date.format(filemod, '%d-%b-%Y') %]
+ [% date.format(filemod, '%d-%b-%Y', 'en_GB') %]
+
+A fourth parameter allows you to force output in GMT, in the case of
+seconds-since-the-epoch input:
+
+ [% date.format(filemod, '%d-%b-%Y', 'en_GB', 1) %]
+
+Note that in this case, if the local time is not GMT, then also specifying
+'%Z' (time zone) in the format parameter will lead to an extremely
+misleading result.
+
+Any or all of these parameters may be named. Positional parameters
+should always be in the order ($time, $format, $locale).
+
+ [% date.format(format => '%H:%M:%S') %]
+ [% date.format(time => filemod, format => '%H:%M:%S') %]
+ [% date.format(mytime, format => '%H:%M:%S') %]
+ [% date.format(mytime, format => '%H:%M:%S', locale => 'fr_FR') %]
+ [% date.format(mytime, format => '%H:%M:%S', gmt => 1) %]
+ ...etc...
+
+The now() method returns the current system time in seconds since the
+epoch.
+
+ [% date.format(date.now, '%A') %]
+
+The calc() method can be used to create an interface to the Date::Calc
+module (if installed on your system).
+
+ [% calc = date.calc %]
+ [% calc.Monday_of_Week(22, 2001).join('/') %]
+
+The manip() method can be used to create an interface to the Date::Manip
+module (if installed on your system).
+
+ [% manip = date.manip %]
+ [% manip.UnixDate("Noon Yesterday","%Y %b %d %H:%M") %]
+
+=head1 AUTHORS
+
+Thierry-Michel Barral E<lt>kktos@electron-libre.comE<gt> wrote the original
+plugin.
+
+Andy Wardley E<lt>abw@cre.canon.co.ukE<gt> provided some minor
+fixups/enhancements, a test script and documentation.
+
+Mark D. Mills E<lt>mark@hostile.orgE<gt> cloned Date::Manip from the
+cute Date::Calc sub-plugin.
+
+=head1 VERSION
+
+2.66, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000 Thierry-Michel Barral, Andy Wardley.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>, L<POSIX|POSIX>
+
diff --git a/lib/Template/Plugins.pm b/lib/Template/Plugins.pm
new file mode 100644
index 0000000..839c85e
--- /dev/null
+++ b/lib/Template/Plugins.pm
@@ -0,0 +1,1031 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Plugins
+#
+# DESCRIPTION
+# Plugin provider which handles the loading of plugin modules and
+# instantiation of plugin objects.
+#
+# AUTHORS
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Plugins.pm,v 2.65 2003/04/24 09:14:38 abw Exp $
+#
+#============================================================================
+
+package Template::Plugins;
+
+require 5.004;
+
+use strict;
+use base qw( Template::Base );
+use vars qw( $VERSION $DEBUG $STD_PLUGINS );
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.65 $ =~ /(\d+)\.(\d+)/);
+
+$STD_PLUGINS = {
+ 'autoformat' => 'Template::Plugin::Autoformat',
+ 'cgi' => 'Template::Plugin::CGI',
+ 'datafile' => 'Template::Plugin::Datafile',
+ 'date' => 'Template::Plugin::Date',
+ 'debug' => 'Template::Plugin::Debug',
+ 'directory' => 'Template::Plugin::Directory',
+ 'dbi' => 'Template::Plugin::DBI',
+ 'dumper' => 'Template::Plugin::Dumper',
+ 'file' => 'Template::Plugin::File',
+ 'format' => 'Template::Plugin::Format',
+ 'html' => 'Template::Plugin::HTML',
+ 'image' => 'Template::Plugin::Image',
+ 'iterator' => 'Template::Plugin::Iterator',
+ 'pod' => 'Template::Plugin::Pod',
+ 'table' => 'Template::Plugin::Table',
+ 'url' => 'Template::Plugin::URL',
+ 'view' => 'Template::Plugin::View',
+ 'wrap' => 'Template::Plugin::Wrap',
+ 'xmlstyle' => 'Template::Plugin::XML::Style',
+};
+
+
+#========================================================================
+# -- PUBLIC METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# fetch($name, \@args, $context)
+#
+# General purpose method for requesting instantiation of a plugin
+# object. The name of the plugin is passed as the first parameter.
+# The internal FACTORY lookup table is consulted to retrieve the
+# appropriate factory object or class name. If undefined, the _load()
+# method is called to attempt to load the module and return a factory
+# class/object which is then cached for subsequent use. A reference
+# to the calling context should be passed as the third parameter.
+# This is passed to the _load() class method. The new() method is
+# then called against the factory class name or prototype object to
+# instantiate a new plugin object, passing any arguments specified by
+# list reference as the second parameter. e.g. where $factory is the
+# class name 'MyClass', the new() method is called as a class method,
+# $factory->new(...), equivalent to MyClass->new(...) . Where
+# $factory is a prototype object, the new() method is called as an
+# object method, $myobject->new(...). This latter approach allows
+# plugins to act as Singletons, cache shared data, etc.
+#
+# Returns a reference to a plugin, (undef, STATUS_DECLINE) to decline
+# the request or ($error, STATUS_ERROR) on error.
+#------------------------------------------------------------------------
+
+sub fetch {
+ my ($self, $name, $args, $context) = @_;
+ my ($factory, $plugin, $error);
+
+ $self->debug("fetch($name, ",
+ defined $args ? ('[ ', join(', ', @$args), ' ]') : '<no args>', ', ',
+ defined $context ? $context : '<no context>',
+ ')') if $self->{ DEBUG };
+
+ # NOTE:
+ # the $context ref gets passed as the first parameter to all regular
+ # plugins, but not to those loaded via LOAD_PERL; to hack around
+ # this until we have a better implementation, we pass the $args
+ # reference to _load() and let it unshift the first args in the
+ # LOAD_PERL case
+
+ $args ||= [ ];
+ unshift @$args, $context;
+
+ $factory = $self->{ FACTORY }->{ $name } ||= do {
+ ($factory, $error) = $self->_load($name, $context);
+ return ($factory, $error) if $error; ## RETURN
+ $factory;
+ };
+
+ # call the new() method on the factory object or class name
+ eval {
+ if (ref $factory eq 'CODE') {
+ defined( $plugin = &$factory(@$args) )
+ || die "$name plugin failed\n";
+ }
+ else {
+ defined( $plugin = $factory->new(@$args) )
+ || die "$name plugin failed: ", $factory->error(), "\n";
+ }
+ };
+ if ($error = $@) {
+# chomp $error;
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR);
+ }
+
+ return $plugin;
+}
+
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# _init(\%config)
+#
+# Private initialisation method.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $params) = @_;
+ my ($pbase, $plugins, $factory) =
+ @$params{ qw( PLUGIN_BASE PLUGINS PLUGIN_FACTORY ) };
+
+ $plugins ||= { };
+ if (ref $pbase ne 'ARRAY') {
+ $pbase = $pbase ? [ $pbase ] : [ ];
+ }
+ push(@$pbase, 'Template::Plugin');
+
+ $self->{ PLUGIN_BASE } = $pbase;
+ $self->{ PLUGINS } = { %$STD_PLUGINS, %$plugins };
+ $self->{ TOLERANT } = $params->{ TOLERANT } || 0;
+ $self->{ LOAD_PERL } = $params->{ LOAD_PERL } || 0;
+ $self->{ FACTORY } = $factory || { };
+ $self->{ DEBUG } = ( $params->{ DEBUG } || 0 )
+ & Template::Constants::DEBUG_PLUGINS;
+
+ return $self;
+}
+
+
+
+#------------------------------------------------------------------------
+# _load($name, $context)
+#
+# Private method which attempts to load a plugin module and determine the
+# correct factory name or object by calling the load() class method in
+# the loaded module.
+#------------------------------------------------------------------------
+
+sub _load {
+ my ($self, $name, $context) = @_;
+ my ($factory, $module, $base, $pkg, $file, $ok, $error);
+
+ if ($module = $self->{ PLUGINS }->{ $name }) {
+ # plugin module name is explicitly stated in PLUGIN_NAME
+ $pkg = $module;
+ ($file = $module) =~ s|::|/|g;
+ $file =~ s|::|/|g;
+ $self->debug("loading $module.pm (PLUGIN_NAME)")
+ if $self->{ DEBUG };
+ $ok = eval { require "$file.pm" };
+ $error = $@;
+ }
+ else {
+ # try each of the PLUGIN_BASE values to build module name
+ ($module = $name) =~ s/\./::/g;
+
+ foreach $base (@{ $self->{ PLUGIN_BASE } }) {
+ $pkg = $base . '::' . $module;
+ ($file = $pkg) =~ s|::|/|g;
+
+ $self->debug("loading $file.pm (PLUGIN_BASE)")
+ if $self->{ DEBUG };
+
+ $ok = eval { require "$file.pm" };
+ last unless $@;
+
+ $error .= "$@\n"
+ unless ($@ =~ /^Can\'t locate $file\.pm/);
+ }
+ }
+
+ if ($ok) {
+ $self->debug("calling $pkg->load()") if $self->{ DEBUG };
+
+ $factory = eval { $pkg->load($context) };
+ $error = '';
+ if ($@ || ! $factory) {
+ $error = $@ || 'load() returned a false value';
+ }
+ }
+ elsif ($self->{ LOAD_PERL }) {
+ # fallback - is it a regular Perl module?
+ ($file = $module) =~ s|::|/|g;
+ eval { require "$file.pm" };
+ if ($@) {
+ $error = $@;
+ }
+ else {
+ # this is a regular Perl module so the new() constructor
+ # isn't expecting a $context reference as the first argument;
+ # so we construct a closure which removes it before calling
+ # $module->new(@_);
+ $factory = sub {
+ shift;
+ $module->new(@_);
+ };
+ $error = '';
+ }
+ }
+
+ if ($factory) {
+ $self->debug("$name => $factory") if $self->{ DEBUG };
+ return $factory;
+ }
+ elsif ($error) {
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR);
+ }
+ else {
+ return (undef, Template::Constants::STATUS_DECLINED);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which constructs and returns text representing the current
+# state of the object.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $output = "[Template::Plugins] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ foreach $key (qw( TOLERANT LOAD_PERL )) {
+ $output .= sprintf($format, $key, $self->{ $key });
+ }
+
+ local $" = ', ';
+ my $fkeys = join(", ", keys %{$self->{ FACTORY }});
+ my $plugins = $self->{ PLUGINS };
+ $plugins = join('', map {
+ sprintf(" $format", $_, $plugins->{ $_ });
+ } keys %$plugins);
+ $plugins = "{\n$plugins }";
+
+ $output .= sprintf($format, 'PLUGIN_BASE', "[ @{ $self->{ PLUGIN_BASE } } ]");
+ $output .= sprintf($format, 'PLUGINS', $plugins);
+ $output .= sprintf($format, 'FACTORY', $fkeys);
+ $output .= '}';
+ return $output;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Plugins - Plugin provider module
+
+=head1 SYNOPSIS
+
+ use Template::Plugins;
+
+ $plugin_provider = Template::Plugins->new(\%options);
+
+ ($plugin, $error) = $plugin_provider->fetch($name, @args);
+
+=head1 DESCRIPTION
+
+The Template::Plugins module defines a provider class which can be used
+to load and instantiate Template Toolkit plugin modules.
+
+=head1 METHODS
+
+=head2 new(\%params)
+
+Constructor method which instantiates and returns a reference to a
+Template::Plugins object. A reference to a hash array of configuration
+items may be passed as a parameter. These are described below.
+
+Note that the Template.pm front-end module creates a Template::Plugins
+provider, passing all configuration items. Thus, the examples shown
+below in the form:
+
+ $plugprov = Template::Plugins->new({
+ PLUGIN_BASE => 'MyTemplate::Plugin',
+ LOAD_PERL => 1,
+ ...
+ });
+
+can also be used via the Template module as:
+
+ $ttengine = Template->new({
+ PLUGIN_BASE => 'MyTemplate::Plugin',
+ LOAD_PERL => 1,
+ ...
+ });
+
+as well as the more explicit form of:
+
+ $plugprov = Template::Plugins->new({
+ PLUGIN_BASE => 'MyTemplate::Plugin',
+ LOAD_PERL => 1,
+ ...
+ });
+
+ $ttengine = Template->new({
+ LOAD_PLUGINS => [ $plugprov ],
+ });
+
+=head2 fetch($name, @args)
+
+Called to request that a plugin of a given name be provided. The relevant
+module is first loaded (if necessary) and the load() class method called
+to return the factory class name (usually the same package name) or a
+factory object (a prototype). The new() method is then called as a
+class or object method against the factory, passing all remaining
+parameters.
+
+Returns a reference to a new plugin object or ($error, STATUS_ERROR)
+on error. May also return (undef, STATUS_DECLINED) to decline to
+serve the request. If TOLERANT is set then all errors will be
+returned as declines.
+
+=head1 CONFIGURATION OPTIONS
+
+The following list details the configuration options that can be provided
+to the Template::Plugins new() constructor.
+
+=over 4
+
+
+
+
+=item PLUGINS
+
+The PLUGINS options can be used to provide a reference to a hash array
+that maps plugin names to Perl module names. A number of standard
+plugins are defined (e.g. 'table', 'cgi', 'dbi', etc.) which map to
+their corresponding Template::Plugin::* counterparts. These can be
+redefined by values in the PLUGINS hash.
+
+ my $plugins = Template::Plugins->new({
+ PLUGINS => {
+ cgi => 'MyOrg::Template::Plugin::CGI',
+ foo => 'MyOrg::Template::Plugin::Foo',
+ bar => 'MyOrg::Template::Plugin::Bar',
+ },
+ });
+
+The USE directive is used to create plugin objects and does so by
+calling the plugin() method on the current Template::Context object.
+If the plugin name is defined in the PLUGINS hash then the
+corresponding Perl module is loaded via require(). The context then
+calls the load() class method which should return the class name
+(default and general case) or a prototype object against which the
+new() method can be called to instantiate individual plugin objects.
+
+If the plugin name is not defined in the PLUGINS hash then the PLUGIN_BASE
+and/or LOAD_PERL options come into effect.
+
+
+
+
+
+=item PLUGIN_BASE
+
+If a plugin is not defined in the PLUGINS hash then the PLUGIN_BASE is used
+to attempt to construct a correct Perl module name which can be successfully
+loaded.
+
+The PLUGIN_BASE can be specified as a single value or as a reference
+to an array of multiple values. The default PLUGIN_BASE value,
+'Template::Plugin', is always added the the end of the PLUGIN_BASE
+list (a single value is first converted to a list). Each value should
+contain a Perl package name to which the requested plugin name is
+appended.
+
+example 1:
+
+ my $plugins = Template::Plugins->new({
+ PLUGIN_BASE => 'MyOrg::Template::Plugin',
+ });
+
+ [% USE Foo %] # => MyOrg::Template::Plugin::Foo
+ or Template::Plugin::Foo
+
+example 2:
+
+ my $plugins = Template::Plugins->new({
+ PLUGIN_BASE => [ 'MyOrg::Template::Plugin',
+ 'YourOrg::Template::Plugin' ],
+ });
+
+ [% USE Foo %] # => MyOrg::Template::Plugin::Foo
+ or YourOrg::Template::Plugin::Foo
+ or Template::Plugin::Foo
+
+
+
+
+
+
+=item LOAD_PERL
+
+If a plugin cannot be loaded using the PLUGINS or PLUGIN_BASE
+approaches then the provider can make a final attempt to load the
+module without prepending any prefix to the module path. This allows
+regular Perl modules (i.e. those that don't reside in the
+Template::Plugin or some other such namespace) to be loaded and used
+as plugins.
+
+By default, the LOAD_PERL option is set to 0 and no attempt will be made
+to load any Perl modules that aren't named explicitly in the PLUGINS
+hash or reside in a package as named by one of the PLUGIN_BASE
+components.
+
+Plugins loaded using the PLUGINS or PLUGIN_BASE receive a reference to
+the current context object as the first argument to the new()
+constructor. Modules loaded using LOAD_PERL are assumed to not
+conform to the plugin interface. They must provide a new() class
+method for instantiating objects but it will not receive a reference
+to the context as the first argument. Plugin modules should provide a
+load() class method (or inherit the default one from the
+Template::Plugin base class) which is called the first time the plugin
+is loaded. Regular Perl modules need not. In all other respects,
+regular Perl objects and Template Toolkit plugins are identical.
+
+If a particular Perl module does not conform to the common, but not
+unilateral, new() constructor convention then a simple plugin wrapper
+can be written to interface to it.
+
+
+
+
+=item TOLERANT
+
+The TOLERANT flag is used by the various Template Toolkit provider
+modules (Template::Provider, Template::Plugins, Template::Filters) to
+control their behaviour when errors are encountered. By default, any
+errors are reported as such, with the request for the particular
+resource (template, plugin, filter) being denied and an exception
+raised. When the TOLERANT flag is set to any true values, errors will
+be silently ignored and the provider will instead return
+STATUS_DECLINED. This allows a subsequent provider to take
+responsibility for providing the resource, rather than failing the
+request outright. If all providers decline to service the request,
+either through tolerated failure or a genuine disinclination to
+comply, then a 'E<lt>resourceE<gt> not found' exception is raised.
+
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging messages from the
+Template::Plugins module by setting it to include the DEBUG_PLUGINS
+value.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_FILTERS | DEBUG_PLUGINS,
+ });
+
+
+
+
+=back
+
+
+
+=head1 TEMPLATE TOOLKIT PLUGINS
+
+The following plugin modules are distributed with the Template
+Toolkit. Some of the plugins interface to external modules (detailed
+below) which should be downloaded from any CPAN site and installed
+before using the plugin.
+
+=head2 Autoformat
+
+The Autoformat plugin is an interface to Damian Conway's Text::Autoformat
+Perl module which provides advanced text wrapping and formatting. See
+L<Template::Plugin::Autoformat> and L<Text::Autoformat> for further
+details.
+
+ [% USE autoformat(left=10, right=20) %]
+ [% autoformat(mytext) %] # call autoformat sub
+ [% mytext FILTER autoformat %] # or use autoformat filter
+
+The Text::Autoformat module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/Text/
+
+=head2 CGI
+
+The CGI plugin is a wrapper around Lincoln Stein's
+E<lt>lstein@genome.wi.mit.eduE<gt> CGI.pm module. The plugin is
+distributed with the Template Toolkit (see L<Template::Plugin::CGI>)
+and the CGI module itself is distributed with recent versions Perl,
+or is available from CPAN.
+
+ [% USE CGI %]
+ [% CGI.param('param_name') %]
+ [% CGI.start_form %]
+ [% CGI.popup_menu( Name => 'color',
+ Values => [ 'Green', 'Brown' ] ) %]
+ [% CGI.end_form %]
+
+=head2 Datafile
+
+Provides an interface to data stored in a plain text file in a simple
+delimited format. The first line in the file specifies field names
+which should be delimiter by any non-word character sequence.
+Subsequent lines define data using the same delimiter as int he first
+line. Blank lines and comments (lines starting '#') are ignored. See
+L<Template::Plugin::Datafile> for further details.
+
+/tmp/mydata:
+
+ # define names for each field
+ id : email : name : tel
+ # here's the data
+ fred : fred@here.com : Fred Smith : 555-1234
+ bill : bill@here.com : Bill White : 555-5678
+
+example:
+
+ [% USE userlist = datafile('/tmp/mydata') %]
+
+ [% FOREACH user = userlist %]
+ [% user.name %] ([% user.id %])
+ [% END %]
+
+=head2 Date
+
+The Date plugin provides an easy way to generate formatted time and date
+strings by delegating to the POSIX strftime() routine. See
+L<Template::Plugin::Date> and L<POSIX> for further details.
+
+ [% USE date %]
+ [% date.format %] # current time/date
+
+ File last modified: [% date.format(template.modtime) %]
+
+=head2 Directory
+
+The Directory plugin provides a simple interface to a directory and
+the files within it. See L<Template::Plugin::Directory> for further
+details.
+
+ [% USE dir = Directory('/tmp') %]
+ [% FOREACH file = dir.files %]
+ # all the plain files in the directory
+ [% END %]
+ [% FOREACH file = dir.dirs %]
+ # all the sub-directories
+ [% END %]
+
+=head2 DBI
+
+The DBI plugin, developed by Simon Matthews
+E<lt>sam@knowledgepool.comE<gt>, brings the full power of Tim Bunce's
+E<lt>Tim.Bunce@ig.co.ukE<gt> database interface module (DBI) to your
+templates. See L<Template::Plugin::DBI> and L<DBI> for further details.
+
+ [% USE DBI('dbi:driver:database', 'user', 'pass') %]
+
+ [% FOREACH user = DBI.query( 'SELECT * FROM users' ) %]
+ [% user.id %] [% user.name %]
+ [% END %]
+
+The DBI and relevant DBD modules are available from CPAN:
+
+ http://www.cpan.org/modules/by-module/DBI/
+
+=head2 Dumper
+
+The Dumper plugin provides an interface to the Data::Dumper module. See
+L<Template::Plugin::Dumper> and L<Data::Dumper> for futher details.
+
+ [% USE dumper(indent=0, pad="<br>") %]
+ [% dumper.dump(myvar, yourvar) %]
+
+=head2 File
+
+The File plugin provides a general abstraction for files and can be
+used to fetch information about specific files within a filesystem.
+See L<Template::Plugin::File> for further details.
+
+ [% USE File('/tmp/foo.html') %]
+ [% File.name %] # foo.html
+ [% File.dir %] # /tmp
+ [% File.mtime %] # modification time
+
+=head2 Filter
+
+This module implements a base class plugin which can be subclassed
+to easily create your own modules that define and install new filters.
+
+ package MyOrg::Template::Plugin::MyFilter;
+
+ use Template::Plugin::Filter;
+ use base qw( Template::Plugin::Filter );
+
+ sub filter {
+ my ($self, $text) = @_;
+
+ # ...mungify $text...
+
+ return $text;
+ }
+
+ # now load it...
+ [% USE MyFilter %]
+
+ # ...and use the returned object as a filter
+ [% FILTER $MyFilter %]
+ ...
+ [% END %]
+
+See L<Template::Plugin::Filter> for further details.
+
+=head2 Format
+
+The Format plugin provides a simple way to format text according to a
+printf()-like format. See L<Template::Plugin::Format> for further
+details.
+
+ [% USE bold = format('<b>%s</b>') %]
+ [% bold('Hello') %]
+
+=head2 GD::Image, GD::Polygon, GD::Constants
+
+These plugins provide access to the GD graphics library via Lincoln
+D. Stein's GD.pm interface. These plugins allow PNG, JPEG and other
+graphical formats to be generated.
+
+ [% FILTER null;
+ USE im = GD.Image(100,100);
+ # allocate some colors
+ black = im.colorAllocate(0, 0, 0);
+ red = im.colorAllocate(255,0, 0);
+ blue = im.colorAllocate(0, 0, 255);
+ # Draw a blue oval
+ im.arc(50,50,95,75,0,360,blue);
+ # And fill it with red
+ im.fill(50,50,red);
+ # Output image in PNG format
+ im.png | stdout(1);
+ END;
+ -%]
+
+See L<Template::Plugin::GD::Image> for further details.
+
+=head2 GD::Text, GD::Text::Align, GD::Text::Wrap
+
+These plugins provide access to Martien Verbruggen's GD::Text,
+GD::Text::Align and GD::Text::Wrap modules. These plugins allow the
+layout, alignment and wrapping of text when drawing text in GD images.
+
+ [% FILTER null;
+ USE gd = GD.Image(200,400);
+ USE gdc = GD.Constants;
+ black = gd.colorAllocate(0, 0, 0);
+ green = gd.colorAllocate(0, 255, 0);
+ txt = "This is some long text. " | repeat(10);
+ USE wrapbox = GD.Text.Wrap(gd,
+ line_space => 4,
+ color => green,
+ text => txt,
+ );
+ wrapbox.set_font(gdc.gdMediumBoldFont);
+ wrapbox.set(align => 'center', width => 160);
+ wrapbox.draw(20, 20);
+ gd.png | stdout(1);
+ END;
+ -%]
+
+See L<Template::Plugin::GD::Text>, L<Template::Plugin::GD::Text::Align>
+and L<Template::Plugin::GD::Text::Wrap> for further details.
+
+=head2 GD::Graph::lines, GD::Graph::bars, GD::Graph::points, GD::Graph::linespoin
+ts, GD::Graph::area, GD::Graph::mixed, GD::Graph::pie
+
+These plugins provide access to Martien Verbruggen's GD::Graph module
+that allows graphs, plots and charts to be created. These plugins allow
+graphs, plots and charts to be generated in PNG, JPEG and other
+graphical formats.
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th"],
+ [ 4, 2, 3, 4, 3, 3.5]
+ ];
+ USE my_graph = GD.Graph.pie(250, 200);
+ my_graph.set(
+ title => 'A Pie Chart',
+ label => 'Label',
+ axislabelclr => 'black',
+ pie_height => 36,
+ transparent => 0,
+ );
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+See
+L<Template::Plugin::GD::Graph::lines>,
+L<Template::Plugin::GD::Graph::bars>,
+L<Template::Plugin::GD::Graph::points>,
+L<Template::Plugin::GD::Graph::linespoints>,
+L<Template::Plugin::GD::Graph::area>,
+L<Template::Plugin::GD::Graph::mixed>,
+L<Template::Plugin::GD::Graph::pie>, and
+L<GD::Graph>,
+for more details.
+
+=head2 GD::Graph::bars3d, GD::Graph::lines3d, GD::Graph::pie3d
+
+These plugins provide access to Jeremy Wadsack's GD::Graph3d
+module. This allows 3D bar charts and 3D lines plots to
+be generated.
+
+ [% FILTER null;
+ data = [
+ ["1st","2nd","3rd","4th","5th","6th","7th", "8th", "9th"],
+ [ 1, 2, 5, 6, 3, 1.5, 1, 3, 4],
+ ];
+ USE my_graph = GD.Graph.bars3d();
+ my_graph.set(
+ x_label => 'X Label',
+ y_label => 'Y label',
+ title => 'A 3d Bar Chart',
+ y_max_value => 8,
+ y_tick_number => 8,
+ y_label_skip => 2,
+ # shadows
+ bar_spacing => 8,
+ shadow_depth => 4,
+ shadowclr => 'dred',
+ transparent => 0,
+ my_graph.plot(data).png | stdout(1);
+ END;
+ -%]
+
+See
+L<Template::Plugin::GD::Graph::lines3d>,
+L<Template::Plugin::GD::Graph::bars3d>, and
+L<Template::Plugin::GD::Graph::pie3d>
+for more details.
+
+=head2 HTML
+
+The HTML plugin is very new and very basic, implementing a few useful
+methods for generating HTML. It is likely to be extended in the future
+or integrated with a larger project to generate HTML elements in a generic
+way (as discussed recently on the mod_perl mailing list).
+
+ [% USE HTML %]
+ [% HTML.escape("if (a < b && c > d) ..." %]
+ [% HTML.attributes(border => 1, cellpadding => 2) %]
+ [% HTML.element(table => { border => 1, cellpadding => 2 }) %]
+
+See L<Template::Plugin::HTML> for further details.
+
+=head2 Iterator
+
+The Iterator plugin provides a way to create a Template::Iterator
+object to iterate over a data set. An iterator is created
+automatically by the FOREACH directive and is aliased to the 'loop'
+variable. This plugin allows an iterator to be explicitly created
+with a given name, or the default plugin name, 'iterator'. See
+L<Template::Plugin::Iterator> for further details.
+
+ [% USE iterator(list, args) %]
+
+ [% FOREACH item = iterator %]
+ [% '<ul>' IF iterator.first %]
+ <li>[% item %]
+ [% '</ul>' IF iterator.last %]
+ [% END %]
+
+=head2 Pod
+
+This plugin provides an interface to the L<Pod::POM|Pod::POM> module
+which parses POD documents into an internal object model which can
+then be traversed and presented through the Template Toolkit.
+
+ [% USE Pod(podfile) %]
+
+ [% FOREACH head1 = Pod.head1;
+ FOREACH head2 = head1/head2;
+ ...
+ END;
+ END
+ %]
+
+=head2 String
+
+The String plugin implements an object-oriented interface for
+manipulating strings. See L<Template::Plugin::String> for further
+details.
+
+ [% USE String 'Hello' %]
+ [% String.append(' World') %]
+
+ [% msg = String.new('Another string') %]
+ [% msg.replace('string', 'text') %]
+
+ The string "[% msg %]" is [% msg.length %] characters long.
+
+=head2 Table
+
+The Table plugin allows you to format a list of data items into a
+virtual table by specifying a fixed number of rows or columns, with
+an optional overlap. See L<Template::Plugin::Table> for further
+details.
+
+ [% USE table(list, rows=10, overlap=1) %]
+
+ [% FOREACH item = table.col(3) %]
+ [% item %]
+ [% END %]
+
+=head2 URL
+
+The URL plugin provides a simple way of contructing URLs from a base
+part and a variable set of parameters. See L<Template::Plugin::URL>
+for further details.
+
+ [% USE mycgi = url('/cgi-bin/bar.pl', debug=1) %]
+
+ [% mycgi %]
+ # ==> /cgi/bin/bar.pl?debug=1
+
+ [% mycgi(mode='submit') %]
+ # ==> /cgi/bin/bar.pl?mode=submit&debug=1
+
+=head2 Wrap
+
+The Wrap plugin uses the Text::Wrap module by David Muir Sharnoff
+E<lt>muir@idiom.comE<gt> (with help from Tim Pierce and many many others)
+to provide simple paragraph formatting. See L<Template::Plugin::Wrap>
+and L<Text::Wrap> for further details.
+
+ [% USE wrap %]
+ [% wrap(mytext, 40, '* ', ' ') %] # use wrap sub
+ [% mytext FILTER wrap(40) -%] # or wrap FILTER
+
+The Text::Wrap module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/Text/
+
+=head2 XML::DOM
+
+The XML::DOM plugin gives access to the XML Document Object Module via
+Clark Cooper E<lt>cooper@sch.ge.comE<gt> and Enno Derksen's
+E<lt>enno@att.comE<gt> XML::DOM module. See L<Template::Plugin::XML::DOM>
+and L<XML::DOM> for further details.
+
+ [% USE dom = XML.DOM %]
+ [% doc = dom.parse(filename) %]
+
+ [% FOREACH node = doc.getElementsByTagName('CODEBASE') %]
+ * [% node.getAttribute('href') %]
+ [% END %]
+
+The plugin requires the XML::DOM module, available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+=head2 XML::RSS
+
+The XML::RSS plugin is a simple interface to Jonathan Eisenzopf's
+E<lt>eisen@pobox.comE<gt> XML::RSS module. A RSS (Rich Site Summary)
+file is typically used to store short news 'headlines' describing
+different links within a site. This plugin allows you to parse RSS
+files and format the contents accordingly using templates.
+See L<Template::Plugin::XML::RSS> and L<XML::RSS> for further details.
+
+ [% USE news = XML.RSS(filename) %]
+
+ [% FOREACH item = news.items %]
+ <a href="[% item.link %]">[% item.title %]</a>
+ [% END %]
+
+The XML::RSS module is available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+=head2 XML::Simple
+
+This plugin implements an interface to the L<XML::Simple|XML::Simple>
+module.
+
+ [% USE xml = XML.Simple(xml_file_or_text) %]
+
+ [% xml.head.title %]
+
+See L<Template::Plugin::XML::Simple> for further details.
+
+=head2 XML::Style
+
+This plugin defines a filter for performing simple stylesheet based
+transformations of XML text.
+
+ [% USE xmlstyle
+ table = {
+ attributes = {
+ border = 0
+ cellpadding = 4
+ cellspacing = 1
+ }
+ }
+ %]
+
+ [% FILTER xmlstyle %]
+ <table>
+ <tr>
+ <td>Foo</td> <td>Bar</td> <td>Baz</td>
+ </tr>
+ </table>
+ [% END %]
+
+See L<Template::Plugin::XML::Style> for further details.
+
+=head2 XML::XPath
+
+The XML::XPath plugin provides an interface to Matt Sergeant's
+E<lt>matt@sergeant.orgE<gt> XML::XPath module. See
+L<Template::Plugin::XML::XPath> and L<XML::XPath> for further details.
+
+ [% USE xpath = XML.XPath(xmlfile) %]
+ [% FOREACH page = xpath.findnodes('/html/body/page') %]
+ [% page.getAttribute('title') %]
+ [% END %]
+
+The plugin requires the XML::XPath module, available from CPAN:
+
+ http://www.cpan.org/modules/by-module/XML/
+
+
+
+
+=head1 BUGS / ISSUES
+
+=over 4
+
+=item *
+
+It might be worthwhile being able to distinguish between absolute
+module names and those which should be applied relative to PLUGIN_BASE
+directories. For example, use 'MyNamespace::MyModule' to denote
+absolute module names (e.g. LOAD_PERL), and 'MyNamespace.MyModule' to
+denote relative to PLUGIN_BASE.
+
+=back
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.65, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Plugin|Template::Plugin>, L<Template::Context|Template::Context>
diff --git a/lib/Template/Provider.pm b/lib/Template/Provider.pm
new file mode 100644
index 0000000..ee599de
--- /dev/null
+++ b/lib/Template/Provider.pm
@@ -0,0 +1,1433 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Provider
+#
+# DESCRIPTION
+# This module implements a class which handles the loading, compiling
+# and caching of templates. Multiple Template::Provider objects can
+# be stacked and queried in turn to effect a Chain-of-Command between
+# them. A provider will attempt to return the requested template,
+# an error (STATUS_ERROR) or decline to provide the template
+# (STATUS_DECLINE), allowing subsequent providers to attempt to
+# deliver it. See 'Design Patterns' for further details.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# TODO:
+# * optional provider prefix (e.g. 'http:')
+# * fold ABSOLUTE and RELATIVE test cases into one regex?
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Provider.pm,v 2.70 2003/04/24 09:14:38 abw Exp $
+#
+#============================================================================
+
+package Template::Provider;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $ERROR $DOCUMENT $STAT_TTL $MAX_DIRS );
+use base qw( Template::Base );
+use Template::Config;
+use Template::Constants;
+use Template::Document;
+use File::Basename;
+use File::Spec;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.70 $ =~ /(\d+)\.(\d+)/);
+
+# name of document class
+$DOCUMENT = 'Template::Document' unless defined $DOCUMENT;
+
+# maximum time between performing stat() on file to check staleness
+$STAT_TTL = 1 unless defined $STAT_TTL;
+
+# maximum number of directories in an INCLUDE_PATH, to prevent runaways
+$MAX_DIRS = 64 unless defined $MAX_DIRS;
+
+use constant PREV => 0;
+use constant NAME => 1;
+use constant DATA => 2;
+use constant LOAD => 3;
+use constant NEXT => 4;
+use constant STAT => 5;
+
+$DEBUG = 0 unless defined $DEBUG;
+
+#========================================================================
+# -- PUBLIC METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# fetch($name)
+#
+# Returns a compiled template for the name specified by parameter.
+# The template is returned from the internal cache if it exists, or
+# loaded and then subsequently cached. The ABSOLUTE and RELATIVE
+# configuration flags determine if absolute (e.g. '/something...')
+# and/or relative (e.g. './something') paths should be honoured. The
+# INCLUDE_PATH is otherwise used to find the named file. $name may
+# also be a reference to a text string containing the template text,
+# or a file handle from which the content is read. The compiled
+# template is not cached in these latter cases given that there is no
+# filename to cache under. A subsequent call to store($name,
+# $compiled) can be made to cache the compiled template for future
+# fetch() calls, if necessary.
+#
+# Returns a compiled template or (undef, STATUS_DECLINED) if the
+# template could not be found. On error (e.g. the file was found
+# but couldn't be read or parsed), the pair ($error, STATUS_ERROR)
+# is returned. The TOLERANT configuration option can be set to
+# downgrade any errors to STATUS_DECLINE.
+#------------------------------------------------------------------------
+
+sub fetch {
+ my ($self, $name) = @_;
+ my ($data, $error);
+
+ if (ref $name) {
+ # $name can be a reference to a scalar, GLOB or file handle
+ ($data, $error) = $self->_load($name);
+ ($data, $error) = $self->_compile($data)
+ unless $error;
+ $data = $data->{ data }
+ unless $error;
+ }
+ elsif (File::Spec->file_name_is_absolute($name)) {
+ # absolute paths (starting '/') allowed if ABSOLUTE set
+ ($data, $error) = $self->{ ABSOLUTE }
+ ? $self->_fetch($name)
+ : $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ("$name: absolute paths are not allowed (set ABSOLUTE option)",
+ Template::Constants::STATUS_ERROR);
+ }
+ elsif ($name =~ m[^\.+/]) {
+ # anything starting "./" is relative to cwd, allowed if RELATIVE set
+ ($data, $error) = $self->{ RELATIVE }
+ ? $self->_fetch($name)
+ : $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ("$name: relative paths are not allowed (set RELATIVE option)",
+ Template::Constants::STATUS_ERROR);
+ }
+ else {
+ # otherwise, it's a file name relative to INCLUDE_PATH
+ ($data, $error) = $self->{ INCLUDE_PATH }
+ ? $self->_fetch_path($name)
+ : (undef, Template::Constants::STATUS_DECLINED);
+ }
+
+# $self->_dump_cache()
+# if $DEBUG > 1;
+
+ return ($data, $error);
+}
+
+
+#------------------------------------------------------------------------
+# store($name, $data)
+#
+# Store a compiled template ($data) in the cached as $name.
+#------------------------------------------------------------------------
+
+sub store {
+ my ($self, $name, $data) = @_;
+ $self->_store($name, {
+ data => $data,
+ load => 0,
+ });
+}
+
+
+#------------------------------------------------------------------------
+# load($name)
+#
+# Load a template without parsing/compiling it, suitable for use with
+# the INSERT directive. There's some duplication with fetch() and at
+# some point this could be reworked to integrate them a little closer.
+#------------------------------------------------------------------------
+
+sub load {
+ my ($self, $name) = @_;
+ my ($data, $error);
+ my $path = $name;
+
+ if (File::Spec->file_name_is_absolute($name)) {
+ # absolute paths (starting '/') allowed if ABSOLUTE set
+ $error = "$name: absolute paths are not allowed (set ABSOLUTE option)"
+ unless $self->{ ABSOLUTE };
+ }
+ elsif ($name =~ m[^\.+/]) {
+ # anything starting "./" is relative to cwd, allowed if RELATIVE set
+ $error = "$name: relative paths are not allowed (set RELATIVE option)"
+ unless $self->{ RELATIVE };
+ }
+ else {
+ INCPATH: {
+ # otherwise, it's a file name relative to INCLUDE_PATH
+ my $paths = $self->paths()
+ || return ($self->error(), Template::Constants::STATUS_ERROR);
+
+ foreach my $dir (@$paths) {
+ $path = "$dir/$name";
+ last INCPATH
+ if -f $path;
+ }
+ undef $path; # not found
+ }
+ }
+
+ if (defined $path && ! $error) {
+ local $/ = undef; # slurp files in one go
+ local *FH;
+ if (open(FH, $path)) {
+ $data = <FH>;
+ close(FH);
+ }
+ else {
+ $error = "$name: $!";
+ }
+ }
+
+ if ($error) {
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR);
+ }
+ elsif (! defined $path) {
+ return (undef, Template::Constants::STATUS_DECLINED);
+ }
+ else {
+ return ($data, Template::Constants::STATUS_OK);
+ }
+}
+
+
+
+#------------------------------------------------------------------------
+# include_path(\@newpath)
+#
+# Accessor method for the INCLUDE_PATH setting. If called with an
+# argument, this method will replace the existing INCLUDE_PATH with
+# the new value.
+#------------------------------------------------------------------------
+
+sub include_path {
+ my ($self, $path) = @_;
+ $self->{ INCLUDE_PATH } = $path if $path;
+ return $self->{ INCLUDE_PATH };
+}
+
+
+#------------------------------------------------------------------------
+# paths()
+#
+# Evaluates the INCLUDE_PATH list, ignoring any blank entries, and
+# calling and subroutine or object references to return dynamically
+# generated path lists. Returns a reference to a new list of paths
+# or undef on error.
+#------------------------------------------------------------------------
+
+sub paths {
+ my $self = shift;
+ my @ipaths = @{ $self->{ INCLUDE_PATH } };
+ my (@opaths, $dpaths, $dir);
+ my $count = $MAX_DIRS;
+
+ while (@ipaths && --$count) {
+ $dir = shift @ipaths || next;
+
+ # $dir can be a sub or object ref which returns a reference
+ # to a dynamically generated list of search paths.
+
+ if (ref $dir eq 'CODE') {
+ eval { $dpaths = &$dir() };
+ if ($@) {
+ chomp $@;
+ return $self->error($@);
+ }
+ unshift(@ipaths, @$dpaths);
+ next;
+ }
+ elsif (UNIVERSAL::can($dir, 'paths')) {
+ $dpaths = $dir->paths()
+ || return $self->error($dir->error());
+ unshift(@ipaths, @$dpaths);
+ next;
+ }
+ else {
+ push(@opaths, $dir);
+ }
+ }
+ return $self->error("INCLUDE_PATH exceeds $MAX_DIRS directories")
+ if @ipaths;
+
+ return \@opaths;
+}
+
+
+#------------------------------------------------------------------------
+# DESTROY
+#
+# The provider cache is implemented as a doubly linked list which Perl
+# cannot free by itself due to the circular references between NEXT <=>
+# PREV items. This cleanup method walks the list deleting all the NEXT/PREV
+# references, allowing the proper cleanup to occur and memory to be
+# repooled.
+#------------------------------------------------------------------------
+
+sub DESTROY {
+ my $self = shift;
+ my ($slot, $next);
+
+ $slot = $self->{ HEAD };
+ while ($slot) {
+ $next = $slot->[ NEXT ];
+ undef $slot->[ PREV ];
+ undef $slot->[ NEXT ];
+ $slot = $next;
+ }
+ undef $self->{ HEAD };
+ undef $self->{ TAIL };
+}
+
+
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# _init()
+#
+# Initialise the cache.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $params) = @_;
+ my $size = $params->{ CACHE_SIZE };
+ my $path = $params->{ INCLUDE_PATH } || '.';
+ my $cdir = $params->{ COMPILE_DIR } || '';
+ my $dlim = $params->{ DELIMITER };
+ my $debug;
+
+ # tweak delim to ignore C:/
+ unless (defined $dlim) {
+ $dlim = ($^O eq 'MSWin32') ? ':(?!\\/)' : ':';
+ }
+
+ # coerce INCLUDE_PATH to an array ref, if not already so
+ $path = [ split(/$dlim/, $path) ]
+ unless ref $path eq 'ARRAY';
+
+ # don't allow a CACHE_SIZE 1 because it breaks things and the
+ # additional checking isn't worth it
+ $size = 2
+ if defined $size && ($size == 1 || $size < 0);
+
+ if (defined ($debug = $params->{ DEBUG })) {
+ $self->{ DEBUG } = $debug & ( Template::Constants::DEBUG_PROVIDER
+ | Template::Constants::DEBUG_FLAGS );
+ }
+ else {
+ $self->{ DEBUG } = $DEBUG;
+ }
+
+ if ($self->{ DEBUG }) {
+ local $" = ', ';
+ $self->debug("creating cache of ",
+ defined $size ? $size : 'unlimited',
+ " slots for [ @$path ]");
+ }
+
+ # create COMPILE_DIR and sub-directories representing each INCLUDE_PATH
+ # element in which to store compiled files
+ if ($cdir) {
+
+# Stas' hack
+# # this is a hack to solve the problem with INCLUDE_PATH using
+# # relative dirs
+# my $segments = 0;
+# for (@$path) {
+# my $c = 0;
+# $c++ while m|\.\.|g;
+# $segments = $c if $c > $segments;
+# }
+# $cdir .= "/".join "/",('hack') x $segments if $segments;
+#
+
+ require File::Path;
+ foreach my $dir (@$path) {
+ next if ref $dir;
+ my $wdir = $dir;
+ $wdir =~ s[:][]g if $^O eq 'MSWin32';
+ $wdir =~ /(.*)/; # untaint
+ &File::Path::mkpath(File::Spec->catfile($cdir, $1));
+ }
+ }
+
+ $self->{ LOOKUP } = { };
+ $self->{ SLOTS } = 0;
+ $self->{ SIZE } = $size;
+ $self->{ INCLUDE_PATH } = $path;
+ $self->{ DELIMITER } = $dlim;
+ $self->{ COMPILE_DIR } = $cdir;
+ $self->{ COMPILE_EXT } = $params->{ COMPILE_EXT } || '';
+ $self->{ ABSOLUTE } = $params->{ ABSOLUTE } || 0;
+ $self->{ RELATIVE } = $params->{ RELATIVE } || 0;
+ $self->{ TOLERANT } = $params->{ TOLERANT } || 0;
+ $self->{ DOCUMENT } = $params->{ DOCUMENT } || $DOCUMENT;
+ $self->{ PARSER } = $params->{ PARSER };
+ $self->{ DEFAULT } = $params->{ DEFAULT };
+# $self->{ PREFIX } = $params->{ PREFIX };
+ $self->{ PARAMS } = $params;
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# _fetch($name)
+#
+# Fetch a file from cache or disk by specification of an absolute or
+# relative filename. No search of the INCLUDE_PATH is made. If the
+# file is found and loaded, it is compiled and cached.
+#------------------------------------------------------------------------
+
+sub _fetch {
+ my ($self, $name) = @_;
+ my $size = $self->{ SIZE };
+ my ($slot, $data, $error);
+
+ $self->debug("_fetch($name)") if $self->{ DEBUG };
+
+ my $compiled = $self->_compiled_filename($name);
+
+ if (defined $size && ! $size) {
+ # caching disabled so load and compile but don't cache
+ if ($compiled && -f $compiled && (stat($name))[9] <= (stat($compiled))[9]) {
+ $data = $self->_load_compiled($compiled);
+ $error = $self->error() unless $data;
+ }
+ else {
+ ($data, $error) = $self->_load($name);
+ ($data, $error) = $self->_compile($data, $compiled)
+ unless $error;
+ $data = $data->{ data }
+ unless $error;
+ }
+ }
+ elsif ($slot = $self->{ LOOKUP }->{ $name }) {
+ # cached entry exists, so refresh slot and extract data
+ ($data, $error) = $self->_refresh($slot);
+ $data = $slot->[ DATA ]
+ unless $error;
+ }
+ else {
+ # nothing in cache so try to load, compile and cache
+ if ($compiled && -f $compiled && (stat($name))[9] <= (stat($compiled))[9]) {
+ $data = $self->_load_compiled($compiled);
+ $error = $self->error() unless $data;
+ $self->store($name, $data) unless $error;
+ }
+ else {
+ ($data, $error) = $self->_load($name);
+ ($data, $error) = $self->_compile($data, $compiled)
+ unless $error;
+ $data = $self->_store($name, $data)
+ unless $error;
+ }
+
+ }
+
+ return ($data, $error);
+}
+
+
+#------------------------------------------------------------------------
+# _fetch_path($name)
+#
+# Fetch a file from cache or disk by specification of an absolute cache
+# name (e.g. 'header') or filename relative to one of the INCLUDE_PATH
+# directories. If the file isn't already cached and can be found and
+# loaded, it is compiled and cached under the full filename.
+#------------------------------------------------------------------------
+
+sub _fetch_path {
+ my ($self, $name) = @_;
+ my ($size, $compext, $compdir) =
+ @$self{ qw( SIZE COMPILE_EXT COMPILE_DIR ) };
+ my ($dir, $paths, $path, $compiled, $slot, $data, $error);
+ local *FH;
+
+ $self->debug("_fetch_path($name)") if $self->{ DEBUG };
+
+ # caching is enabled if $size is defined and non-zero or undefined
+ my $caching = (! defined $size || $size);
+
+ INCLUDE: {
+
+ # the template may have been stored using a non-filename name
+ if ($caching && ($slot = $self->{ LOOKUP }->{ $name })) {
+ # cached entry exists, so refresh slot and extract data
+ ($data, $error) = $self->_refresh($slot);
+ $data = $slot->[ DATA ]
+ unless $error;
+ last INCLUDE;
+ }
+
+ $paths = $self->paths() || do {
+ $error = Template::Constants::STATUS_ERROR;
+ $data = $self->error();
+ last INCLUDE;
+ };
+
+ # search the INCLUDE_PATH for the file, in cache or on disk
+ foreach $dir (@$paths) {
+ $path = "$dir/$name";
+
+ $self->debug("searching path: $path\n") if $self->{ DEBUG };
+
+ if ($caching && ($slot = $self->{ LOOKUP }->{ $path })) {
+ # cached entry exists, so refresh slot and extract data
+ ($data, $error) = $self->_refresh($slot);
+ $data = $slot->[ DATA ]
+ unless $error;
+ last INCLUDE;
+ }
+ elsif (-f $path) {
+ $compiled = $self->_compiled_filename($path)
+ if $compext || $compdir;
+
+ if ($compiled && -f $compiled && (stat($path))[9] <= (stat($compiled))[9]) {
+ if ($data = $self->_load_compiled($compiled)) {
+ # store in cache
+ $data = $self->store($path, $data);
+ $error = Template::Constants::STATUS_OK;
+ last INCLUDE;
+ }
+ else {
+ warn($self->error(), "\n");
+ }
+ }
+ # $compiled is set if an attempt to write the compiled
+ # template to disk should be made
+
+ ($data, $error) = $self->_load($path, $name);
+ ($data, $error) = $self->_compile($data, $compiled)
+ unless $error;
+ $data = $self->_store($path, $data)
+ unless $error || ! $caching;
+ $data = $data->{ data } if ! $caching;
+ # all done if $error is OK or ERROR
+ last INCLUDE if ! $error
+ || $error == Template::Constants::STATUS_ERROR;
+ }
+ }
+ # template not found, so look for a DEFAULT template
+ my $default;
+ if (defined ($default = $self->{ DEFAULT }) && $name ne $default) {
+ $name = $default;
+ redo INCLUDE;
+ }
+ ($data, $error) = (undef, Template::Constants::STATUS_DECLINED);
+ } # INCLUDE
+
+ return ($data, $error);
+}
+
+
+
+sub _compiled_filename {
+ my ($self, $file) = @_;
+ my ($compext, $compdir) = @$self{ qw( COMPILE_EXT COMPILE_DIR ) };
+ my ($path, $compiled);
+
+ return undef
+ unless $compext || $compdir;
+
+ $path = $file;
+ $path =~ /^(.+)$/s or die "invalid filename: $path";
+ $path =~ s[:][]g if $^O eq 'MSWin32';
+
+ $compiled = "$path$compext";
+ $compiled = File::Spec->catfile($compdir, $compiled) if length $compdir;
+
+ return $compiled;
+}
+
+
+sub _load_compiled {
+ my ($self, $file) = @_;
+ my $compiled;
+
+ # load compiled template via require(); we zap any
+ # %INC entry to ensure it is reloaded (we don't
+ # want 1 returned by require() to say it's in memory)
+ delete $INC{ $file };
+ eval { $compiled = require $file; };
+ return $@
+ ? $self->error("compiled template $compiled: $@")
+ : $compiled;
+}
+
+
+
+#------------------------------------------------------------------------
+# _load($name, $alias)
+#
+# Load template text from a string ($name = scalar ref), GLOB or file
+# handle ($name = ref), or from an absolute filename ($name = scalar).
+# Returns a hash array containing the following items:
+# name filename or $alias, if provided, or 'input text', etc.
+# text template text
+# time modification time of file, or current time for handles/strings
+# load time file was loaded (now!)
+#
+# On error, returns ($error, STATUS_ERROR), or (undef, STATUS_DECLINED)
+# if TOLERANT is set.
+#------------------------------------------------------------------------
+
+sub _load {
+ my ($self, $name, $alias) = @_;
+ my ($data, $error);
+ my $tolerant = $self->{ TOLERANT };
+ my $now = time;
+ local $/ = undef; # slurp files in one go
+ local *FH;
+
+ $alias = $name unless defined $alias or ref $name;
+
+ $self->debug("_load($name, ", defined $alias ? $alias : '<no alias>',
+ ')') if $self->{ DEBUG };
+
+ LOAD: {
+ if (ref $name eq 'SCALAR') {
+ # $name can be a SCALAR reference to the input text...
+ $data = {
+ name => defined $alias ? $alias : 'input text',
+ text => $$name,
+ time => $now,
+ load => 0,
+ };
+ }
+ elsif (ref $name) {
+ # ...or a GLOB or file handle...
+ my $text = <$name>;
+ $data = {
+ name => defined $alias ? $alias : 'input file handle',
+ text => $text,
+ time => $now,
+ load => 0,
+ };
+ }
+ elsif (-f $name) {
+ if (open(FH, $name)) {
+ my $text = <FH>;
+ $data = {
+ name => $alias,
+ text => $text,
+ time => (stat $name)[9],
+ load => $now,
+ };
+ }
+ elsif ($tolerant) {
+ ($data, $error) = (undef, Template::Constants::STATUS_DECLINED);
+ }
+ else {
+ $data = "$alias: $!";
+ $error = Template::Constants::STATUS_ERROR;
+ }
+ }
+ else {
+ ($data, $error) = (undef, Template::Constants::STATUS_DECLINED);
+ }
+ }
+
+ return ($data, $error);
+}
+
+
+#------------------------------------------------------------------------
+# _refresh(\@slot)
+#
+# Private method called to mark a cache slot as most recently used.
+# A reference to the slot array should be passed by parameter. The
+# slot is relocated to the head of the linked list. If the file from
+# which the data was loaded has been upated since it was compiled, then
+# it is re-loaded from disk and re-compiled.
+#------------------------------------------------------------------------
+
+sub _refresh {
+ my ($self, $slot) = @_;
+ my ($head, $file, $data, $error);
+
+
+ $self->debug("_refresh([ ",
+ join(', ', map { defined $_ ? $_ : '<undef>' } @$slot),
+ '])') if $self->{ DEBUG };
+
+ # if it's more than $STAT_TTL seconds since we last performed a
+ # stat() on the file then we need to do it again and see if the file
+ # time has changed
+ if ( (time - $slot->[ STAT ]) > $STAT_TTL && stat $slot->[ NAME ] ) {
+ $slot->[ STAT ] = time;
+
+ if ( (stat(_))[9] != $slot->[ LOAD ]) {
+
+ $self->debug("refreshing cache file ", $slot->[ NAME ])
+ if $self->{ DEBUG };
+
+ ($data, $error) = $self->_load($slot->[ NAME ],
+ $slot->[ DATA ]->{ name });
+ ($data, $error) = $self->_compile($data)
+ unless $error;
+
+ unless ($error) {
+ $slot->[ DATA ] = $data->{ data };
+ $slot->[ LOAD ] = $data->{ time };
+ }
+ }
+ }
+
+ unless( $self->{ HEAD } == $slot ) {
+ # remove existing slot from usage chain...
+ if ($slot->[ PREV ]) {
+ $slot->[ PREV ]->[ NEXT ] = $slot->[ NEXT ];
+ }
+ else {
+ $self->{ HEAD } = $slot->[ NEXT ];
+ }
+ if ($slot->[ NEXT ]) {
+ $slot->[ NEXT ]->[ PREV ] = $slot->[ PREV ];
+ }
+ else {
+ $self->{ TAIL } = $slot->[ PREV ];
+ }
+
+ # ..and add to start of list
+ $head = $self->{ HEAD };
+ $head->[ PREV ] = $slot if $head;
+ $slot->[ PREV ] = undef;
+ $slot->[ NEXT ] = $head;
+ $self->{ HEAD } = $slot;
+ }
+
+ return ($data, $error);
+}
+
+
+#------------------------------------------------------------------------
+# _store($name, $data)
+#
+# Private method called to add a data item to the cache. If the cache
+# size limit has been reached then the oldest entry at the tail of the
+# list is removed and its slot relocated to the head of the list and
+# reused for the new data item. If the cache is under the size limit,
+# or if no size limit is defined, then the item is added to the head
+# of the list.
+#------------------------------------------------------------------------
+
+sub _store {
+ my ($self, $name, $data, $compfile) = @_;
+ my $size = $self->{ SIZE };
+ my ($slot, $head);
+
+ # extract the load time and compiled template from the data
+# my $load = $data->{ load };
+ my $load = (stat($name))[9];
+ $data = $data->{ data };
+
+ $self->debug("_store($name, $data)") if $self->{ DEBUG };
+
+ if (defined $size && $self->{ SLOTS } >= $size) {
+ # cache has reached size limit, so reuse oldest entry
+
+ $self->debug("reusing oldest cache entry (size limit reached: $size)\nslots: $self->{ SLOTS }") if $self->{ DEBUG };
+
+ # remove entry from tail of list
+ $slot = $self->{ TAIL };
+ $slot->[ PREV ]->[ NEXT ] = undef;
+ $self->{ TAIL } = $slot->[ PREV ];
+
+ # remove name lookup for old node
+ delete $self->{ LOOKUP }->{ $slot->[ NAME ] };
+
+ # add modified node to head of list
+ $head = $self->{ HEAD };
+ $head->[ PREV ] = $slot if $head;
+ @$slot = ( undef, $name, $data, $load, $head, time );
+ $self->{ HEAD } = $slot;
+
+ # add name lookup for new node
+ $self->{ LOOKUP }->{ $name } = $slot;
+ }
+ else {
+ # cache is under size limit, or none is defined
+
+ $self->debug("adding new cache entry") if $self->{ DEBUG };
+
+ # add new node to head of list
+ $head = $self->{ HEAD };
+ $slot = [ undef, $name, $data, $load, $head, time ];
+ $head->[ PREV ] = $slot if $head;
+ $self->{ HEAD } = $slot;
+ $self->{ TAIL } = $slot unless $self->{ TAIL };
+
+ # add lookup from name to slot and increment nslots
+ $self->{ LOOKUP }->{ $name } = $slot;
+ $self->{ SLOTS }++;
+ }
+
+ return $data;
+}
+
+
+#------------------------------------------------------------------------
+# _compile($data)
+#
+# Private method called to parse the template text and compile it into
+# a runtime form. Creates and delegates a Template::Parser object to
+# handle the compilation, or uses a reference passed in PARSER. On
+# success, the compiled template is stored in the 'data' item of the
+# $data hash and returned. On error, ($error, STATUS_ERROR) is returned,
+# or (undef, STATUS_DECLINED) if the TOLERANT flag is set.
+# The optional $compiled parameter may be passed to specify
+# the name of a compiled template file to which the generated Perl
+# code should be written. Errors are (for now...) silently
+# ignored, assuming that failures to open a file for writing are
+# intentional (e.g directory write permission).
+#------------------------------------------------------------------------
+
+sub _compile {
+ my ($self, $data, $compfile) = @_;
+ my $text = $data->{ text };
+ my ($parsedoc, $error);
+
+ $self->debug("_compile($data, ",
+ defined $compfile ? $compfile : '<no compfile>', ')')
+ if $self->{ DEBUG };
+
+ my $parser = $self->{ PARSER }
+ ||= Template::Config->parser($self->{ PARAMS })
+ || return (Template::Config->error(), Template::Constants::STATUS_ERROR);
+
+ # discard the template text - we don't need it any more
+ delete $data->{ text };
+
+ # call parser to compile template into Perl code
+ if ($parsedoc = $parser->parse($text, $data)) {
+
+ $parsedoc->{ METADATA } = {
+ 'name' => $data->{ name },
+ 'modtime' => $data->{ time },
+ %{ $parsedoc->{ METADATA } },
+ };
+
+ # write the Perl code to the file $compfile, if defined
+ if ($compfile) {
+ my $basedir = &File::Basename::dirname($compfile);
+ $basedir =~ /(.*)/;
+ $basedir = $1;
+ &File::Path::mkpath($basedir) unless -d $basedir;
+
+ my $docclass = $self->{ DOCUMENT };
+ $error = 'cache failed to write '
+ . &File::Basename::basename($compfile)
+ . ': ' . $docclass->error()
+ unless $docclass->write_perl_file($compfile, $parsedoc);
+
+ # set atime and mtime of newly compiled file, don't bother
+ # if time is undef
+ if (!defined($error) && defined $data->{ time }) {
+ my ($cfile) = $compfile =~ /^(.+)$/s or do {
+ return("invalid filename: $compfile",
+ Template::Constants::STATUS_ERROR);
+ };
+
+ my ($ctime) = $data->{ time } =~ /^(\d+)$/;
+ unless ($ctime || $ctime eq 0) {
+ return("invalid time: $ctime",
+ Template::Constants::STATUS_ERROR);
+ }
+ utime($ctime, $ctime, $cfile);
+ }
+ }
+
+ unless ($error) {
+ return $data ## RETURN ##
+ if $data->{ data } = Template::Document->new($parsedoc);
+ $error = $Template::Document::ERROR;
+ }
+ }
+ else {
+ $error = Template::Exception->new( 'parse', "$data->{ name } " .
+ $parser->error() );
+ }
+
+ # return STATUS_ERROR, or STATUS_DECLINED if we're being tolerant
+ return $self->{ TOLERANT }
+ ? (undef, Template::Constants::STATUS_DECLINED)
+ : ($error, Template::Constants::STATUS_ERROR)
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal object
+# state.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $size = $self->{ SIZE };
+ my $parser = $self->{ PARSER };
+ $parser = $parser ? $parser->_dump() : '<no parser>';
+ $parser =~ s/\n/\n /gm;
+ $size = 'unlimited' unless defined $size;
+
+ my $output = "[Template::Provider] {\n";
+ my $format = " %-16s => %s\n";
+ my $key;
+
+ $output .= sprintf($format, 'INCLUDE_PATH',
+ '[ ' . join(', ', @{ $self->{ INCLUDE_PATH } }) . ' ]');
+ $output .= sprintf($format, 'CACHE_SIZE', $size);
+
+ foreach $key (qw( ABSOLUTE RELATIVE TOLERANT DELIMITER
+ COMPILE_EXT COMPILE_DIR )) {
+ $output .= sprintf($format, $key, $self->{ $key });
+ }
+ $output .= sprintf($format, 'PARSER', $parser);
+
+
+ local $" = ', ';
+ my $lookup = $self->{ LOOKUP };
+ $lookup = join('', map {
+ sprintf(" $format", $_, defined $lookup->{ $_ }
+ ? ('[ ' . join(', ', map { defined $_ ? $_ : '<undef>' }
+ @{ $lookup->{ $_ } }) . ' ]') : '<undef>');
+ } sort keys %$lookup);
+ $lookup = "{\n$lookup }";
+
+ $output .= sprintf($format, LOOKUP => $lookup);
+
+ $output .= '}';
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# _dump_cache()
+#
+# Debug method which prints the current state of the cache to STDERR.
+#------------------------------------------------------------------------
+
+sub _dump_cache {
+ my $self = shift;
+ my ($node, $lut, $count);
+
+ $count = 0;
+ if ($node = $self->{ HEAD }) {
+ while ($node) {
+ $lut->{ $node } = $count++;
+ $node = $node->[ NEXT ];
+ }
+ $node = $self->{ HEAD };
+ print STDERR "CACHE STATE:\n";
+ print STDERR " HEAD: ", $self->{ HEAD }->[ NAME ], "\n";
+ print STDERR " TAIL: ", $self->{ TAIL }->[ NAME ], "\n";
+ while ($node) {
+ my ($prev, $name, $data, $load, $next) = @$node;
+# $name = '...' . substr($name, -10) if length $name > 10;
+ $prev = $prev ? "#$lut->{ $prev }<-": '<undef>';
+ $next = $next ? "->#$lut->{ $next }": '<undef>';
+ print STDERR " #$lut->{ $node } : [ $prev, $name, $data, $load, $next ]\n";
+ $node = $node->[ NEXT ];
+ }
+ }
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Provider - Provider module for loading/compiling templates
+
+=head1 SYNOPSIS
+
+ $provider = Template::Provider->new(\%options);
+
+ ($template, $error) = $provider->fetch($name);
+
+=head1 DESCRIPTION
+
+The Template::Provider is used to load, parse, compile and cache template
+documents. This object may be sub-classed to provide more specific
+facilities for loading, or otherwise providing access to templates.
+
+The Template::Context objects maintain a list of Template::Provider
+objects which are polled in turn (via fetch()) to return a requested
+template. Each may return a compiled template, raise an error, or
+decline to serve the reqest, giving subsequent providers a chance to
+do so.
+
+This is the "Chain of Responsiblity" pattern. See 'Design Patterns' for
+further information.
+
+This documentation needs work.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%options)
+
+Constructor method which instantiates and returns a new Template::Provider
+object. The optional parameter may be a hash reference containing any of
+the following items:
+
+=over 4
+
+
+
+
+=item INCLUDE_PATH
+
+The INCLUDE_PATH is used to specify one or more directories in which
+template files are located. When a template is requested that isn't
+defined locally as a BLOCK, each of the INCLUDE_PATH directories is
+searched in turn to locate the template file. Multiple directories
+can be specified as a reference to a list or as a single string where
+each directory is delimited by ':'.
+
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => '/usr/local/templates',
+ });
+
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => '/usr/local/templates:/tmp/my/templates',
+ });
+
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => [ '/usr/local/templates',
+ '/tmp/my/templates' ],
+ });
+
+On Win32 systems, a little extra magic is invoked, ignoring delimiters
+that have ':' followed by a '/' or '\'. This avoids confusion when using
+directory names like 'C:\Blah Blah'.
+
+When specified as a list, the INCLUDE_PATH path can contain elements
+which dynamically generate a list of INCLUDE_PATH directories. These
+generator elements can be specified as a reference to a subroutine or
+an object which implements a paths() method.
+
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => [ '/usr/local/templates',
+ \&incpath_generator,
+ My::IncPath::Generator->new( ... ) ],
+ });
+
+Each time a template is requested and the INCLUDE_PATH examined, the
+subroutine or object method will be called. A reference to a list of
+directories should be returned. Generator subroutines should report
+errors using die(). Generator objects should return undef and make an
+error available via its error() method.
+
+For example:
+
+ sub incpath_generator {
+
+ # ...some code...
+
+ if ($all_is_well) {
+ return \@list_of_directories;
+ }
+ else {
+ die "cannot generate INCLUDE_PATH...\n";
+ }
+ }
+
+or:
+
+ package My::IncPath::Generator;
+
+ # Template::Base (or Class::Base) provides error() method
+ use Template::Base;
+ use base qw( Template::Base );
+
+ sub paths {
+ my $self = shift;
+
+ # ...some code...
+
+ if ($all_is_well) {
+ return \@list_of_directories;
+ }
+ else {
+ return $self->error("cannot generate INCLUDE_PATH...\n");
+ }
+ }
+
+ 1;
+
+
+
+
+
+=item DELIMITER
+
+Used to provide an alternative delimiter character sequence for
+separating paths specified in the INCLUDE_PATH. The default
+value for DELIMITER is ':'.
+
+ # tolerate Silly Billy's file system conventions
+ my $provider = Template::Provider->new({
+ DELIMITER => '; ',
+ INCLUDE_PATH => 'C:/HERE/NOW; D:/THERE/THEN',
+ });
+
+ # better solution: install Linux! :-)
+
+On Win32 systems, the default delimiter is a little more intelligent,
+splitting paths only on ':' characters that aren't followed by a '/'.
+This means that the following should work as planned, splitting the
+INCLUDE_PATH into 2 separate directories, C:/foo and C:/bar.
+
+ # on Win32 only
+ my $provider = Template::Provider->new({
+ INCLUDE_PATH => 'C:/Foo:C:/Bar'
+ });
+
+However, if you're using Win32 then it's recommended that you
+explicitly set the DELIMITER character to something else (e.g. ';')
+rather than rely on this subtle magic.
+
+
+
+
+=item ABSOLUTE
+
+The ABSOLUTE flag is used to indicate if templates specified with
+absolute filenames (e.g. '/foo/bar') should be processed. It is
+disabled by default and any attempt to load a template by such a
+name will cause a 'file' exception to be raised.
+
+ my $provider = Template::Provider->new({
+ ABSOLUTE => 1,
+ });
+
+ # this is why it's disabled by default
+ [% INSERT /etc/passwd %]
+
+On Win32 systems, the regular expression for matching absolute
+pathnames is tweaked slightly to also detect filenames that start
+with a driver letter and colon, such as:
+
+ C:/Foo/Bar
+
+
+
+
+
+
+=item RELATIVE
+
+The RELATIVE flag is used to indicate if templates specified with
+filenames relative to the current directory (e.g. './foo/bar' or
+'../../some/where/else') should be loaded. It is also disabled by
+default, and will raise a 'file' error if such template names are
+encountered.
+
+ my $provider = Template::Provider->new({
+ RELATIVE => 1,
+ });
+
+ [% INCLUDE ../logs/error.log %]
+
+
+
+
+
+=item DEFAULT
+
+The DEFAULT option can be used to specify a default template which should
+be used whenever a specified template can't be found in the INCLUDE_PATH.
+
+ my $provider = Template::Provider->new({
+ DEFAULT => 'notfound.html',
+ });
+
+If a non-existant template is requested through the Template process()
+method, or by an INCLUDE, PROCESS or WRAPPER directive, then the
+DEFAULT template will instead be processed, if defined. Note that the
+DEFAULT template is not used when templates are specified with
+absolute or relative filenames, or as a reference to a input file
+handle or text string.
+
+
+
+
+
+=item CACHE_SIZE
+
+The Template::Provider module caches compiled templates to avoid the need
+to re-parse template files or blocks each time they are used. The CACHE_SIZE
+option is used to limit the number of compiled templates that the module
+should cache.
+
+By default, the CACHE_SIZE is undefined and all compiled templates are
+cached. When set to any positive value, the cache will be limited to
+storing no more than that number of compiled templates. When a new
+template is loaded and compiled and the cache is full (i.e. the number
+of entries == CACHE_SIZE), the least recently used compiled template
+is discarded to make room for the new one.
+
+The CACHE_SIZE can be set to 0 to disable caching altogether.
+
+ my $provider = Template::Provider->new({
+ CACHE_SIZE => 64, # only cache 64 compiled templates
+ });
+
+ my $provider = Template::Provider->new({
+ CACHE_SIZE => 0, # don't cache any compiled templates
+ });
+
+
+
+
+
+
+=item COMPILE_EXT
+
+From version 2 onwards, the Template Toolkit has the ability to
+compile templates to Perl code and save them to disk for subsequent
+use (i.e. cache persistence). The COMPILE_EXT option may be
+provided to specify a filename extension for compiled template files.
+It is undefined by default and no attempt will be made to read or write
+any compiled template files.
+
+ my $provider = Template::Provider->new({
+ COMPILE_EXT => '.ttc',
+ });
+
+If COMPILE_EXT is defined (and COMPILE_DIR isn't, see below) then compiled
+template files with the COMPILE_EXT extension will be written to the same
+directory from which the source template files were loaded.
+
+Compiling and subsequent reuse of templates happens automatically
+whenever the COMPILE_EXT or COMPILE_DIR options are set. The Template
+Toolkit will automatically reload and reuse compiled files when it
+finds them on disk. If the corresponding source file has been modified
+since the compiled version as written, then it will load and re-compile
+the source and write a new compiled version to disk.
+
+This form of cache persistence offers significant benefits in terms of
+time and resources required to reload templates. Compiled templates can
+be reloaded by a simple call to Perl's require(), leaving Perl to handle
+all the parsing and compilation. This is a Good Thing.
+
+=item COMPILE_DIR
+
+The COMPILE_DIR option is used to specify an alternate directory root
+under which compiled template files should be saved.
+
+ my $provider = Template::Provider->new({
+ COMPILE_DIR => '/tmp/ttc',
+ });
+
+The COMPILE_EXT option may also be specified to have a consistent file
+extension added to these files.
+
+ my $provider1 = Template::Provider->new({
+ COMPILE_DIR => '/tmp/ttc',
+ COMPILE_EXT => '.ttc1',
+ });
+
+ my $provider2 = Template::Provider->new({
+ COMPILE_DIR => '/tmp/ttc',
+ COMPILE_EXT => '.ttc2',
+ });
+
+
+When COMPILE_EXT is undefined, the compiled template files have the
+same name as the original template files, but reside in a different
+directory tree.
+
+Each directory in the INCLUDE_PATH is replicated in full beneath the
+COMPILE_DIR directory. This example:
+
+ my $provider = Template::Provider->new({
+ COMPILE_DIR => '/tmp/ttc',
+ INCLUDE_PATH => '/home/abw/templates:/usr/share/templates',
+ });
+
+would create the following directory structure:
+
+ /tmp/ttc/home/abw/templates/
+ /tmp/ttc/usr/share/templates/
+
+Files loaded from different INCLUDE_PATH directories will have their
+compiled forms save in the relevant COMPILE_DIR directory.
+
+On Win32 platforms a filename may by prefixed by a drive letter and
+colon. e.g.
+
+ C:/My Templates/header
+
+The colon will be silently stripped from the filename when it is added
+to the COMPILE_DIR value(s) to prevent illegal filename being generated.
+Any colon in COMPILE_DIR elements will be left intact. For example:
+
+ # Win32 only
+ my $provider = Template::Provider->new({
+ DELIMITER => ';',
+ COMPILE_DIR => 'C:/TT2/Cache',
+ INCLUDE_PATH => 'C:/TT2/Templates;D:/My Templates',
+ });
+
+This would create the following cache directories:
+
+ C:/TT2/Cache/C/TT2/Templates
+ C:/TT2/Cache/D/My Templates
+
+
+
+
+=item TOLERANT
+
+The TOLERANT flag is used by the various Template Toolkit provider
+modules (Template::Provider, Template::Plugins, Template::Filters) to
+control their behaviour when errors are encountered. By default, any
+errors are reported as such, with the request for the particular
+resource (template, plugin, filter) being denied and an exception
+raised. When the TOLERANT flag is set to any true values, errors will
+be silently ignored and the provider will instead return
+STATUS_DECLINED. This allows a subsequent provider to take
+responsibility for providing the resource, rather than failing the
+request outright. If all providers decline to service the request,
+either through tolerated failure or a genuine disinclination to
+comply, then a 'E<lt>resourceE<gt> not found' exception is raised.
+
+
+
+
+
+
+=item PARSER
+
+The Template::Parser module implements a parser object for compiling
+templates into Perl code which can then be executed. A default object
+of this class is created automatically and then used by the
+Template::Provider whenever a template is loaded and requires
+compilation. The PARSER option can be used to provide a reference to
+an alternate parser object.
+
+ my $provider = Template::Provider->new({
+ PARSER => MyOrg::Template::Parser->new({ ... }),
+ });
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging messages from the
+Template::Provider module by setting it to include the DEBUG_PROVIDER
+value.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_PROVIDER,
+ });
+
+
+
+=back
+
+=head2 fetch($name)
+
+Returns a compiled template for the name specified. If the template
+cannot be found then (undef, STATUS_DECLINED) is returned. If an error
+occurs (e.g. read error, parse error) then ($error, STATUS_ERROR) is
+returned, where $error is the error message generated. If the TOLERANT
+flag is set the the method returns (undef, STATUS_DECLINED) instead of
+returning an error.
+
+=head2 store($name, $template)
+
+Stores the compiled template, $template, in the cache under the name,
+$name. Susbequent calls to fetch($name) will return this template in
+preference to any disk-based file.
+
+=head2 include_path(\@newpath))
+
+Accessor method for the INCLUDE_PATH setting. If called with an
+argument, this method will replace the existing INCLUDE_PATH with
+the new value.
+
+=head2 paths()
+
+This method generates a copy of the INCLUDE_PATH list. Any elements in the
+list which are dynamic generators (e.g. references to subroutines or objects
+implementing a paths() method) will be called and the list of directories
+returned merged into the output list.
+
+It is possible to provide a generator which returns itself, thus sending
+this method into an infinite loop. To detect and prevent this from happening,
+the C<$MAX_DIRS> package variable, set to 64 by default, limits the maximum
+number of paths that can be added to, or generated for the output list. If
+this number is exceeded then the method will immediately return an error
+reporting as much.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.70, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Parser|Template::Parser>, L<Template::Context|Template::Context>
diff --git a/lib/Template/Service.pm b/lib/Template/Service.pm
new file mode 100644
index 0000000..e2ac533
--- /dev/null
+++ b/lib/Template/Service.pm
@@ -0,0 +1,765 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Service
+#
+# DESCRIPTION
+# Module implementing a template processing service which wraps a
+# template within PRE_PROCESS and POST_PROCESS templates and offers
+# ERROR recovery.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Service.pm,v 2.70 2003/04/29 12:39:37 abw Exp $
+#
+#============================================================================
+
+package Template::Service;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $ERROR );
+use base qw( Template::Base );
+use Template::Base;
+use Template::Config;
+use Template::Exception;
+use Template::Constants;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.70 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+
+
+#========================================================================
+# ----- PUBLIC METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# process($template, \%params)
+#
+# Process a template within a service framework. A service may encompass
+# PRE_PROCESS and POST_PROCESS templates and an ERROR hash which names
+# templates to be substituted for the main template document in case of
+# error. Each service invocation begins by resetting the state of the
+# context object via a call to reset(). The AUTO_RESET option may be set
+# to 0 (default: 1) to bypass this step.
+#------------------------------------------------------------------------
+
+sub process {
+ my ($self, $template, $params) = @_;
+ my $context = $self->{ CONTEXT };
+ my ($name, $output, $procout, $error);
+ $output = '';
+
+ $self->debug("process($template, ",
+ defined $params ? $params : '<no params>',
+ ')') if $self->{ DEBUG };
+
+ $context->reset()
+ if $self->{ AUTO_RESET };
+
+ # pre-request compiled template from context so that we can alias it
+ # in the stash for pre-processed templates to reference
+ eval { $template = $context->template($template) };
+ return $self->error($@)
+ if $@;
+
+ # localise the variable stash with any parameters passed
+ # and set the 'template' variable
+ $params ||= { };
+ $params->{ template } = $template
+ unless ref $template eq 'CODE';
+ $context->localise($params);
+
+ SERVICE: {
+ # PRE_PROCESS
+ eval {
+ foreach $name (@{ $self->{ PRE_PROCESS } }) {
+ $self->debug("PRE_PROCESS: $name") if $self->{ DEBUG };
+ $output .= $context->process($name);
+ }
+ };
+ last SERVICE if ($error = $@);
+
+ # PROCESS
+ eval {
+ foreach $name (@{ $self->{ PROCESS } || [ $template ] }) {
+ $self->debug("PROCESS: $name") if $self->{ DEBUG };
+ $procout .= $context->process($name);
+ }
+ };
+ if ($error = $@) {
+ last SERVICE
+ unless defined ($procout = $self->_recover(\$error));
+ }
+
+ if (defined $procout) {
+ # WRAPPER
+ eval {
+ foreach $name (reverse @{ $self->{ WRAPPER } }) {
+ $self->debug("WRAPPER: $name") if $self->{ DEBUG };
+ $procout = $context->process($name, { content => $procout });
+ }
+ };
+ last SERVICE if ($error = $@);
+ $output .= $procout;
+ }
+
+ # POST_PROCESS
+ eval {
+ foreach $name (@{ $self->{ POST_PROCESS } }) {
+ $self->debug("POST_PROCESS: $name") if $self->{ DEBUG };
+ $output .= $context->process($name);
+ }
+ };
+ last SERVICE if ($error = $@);
+ }
+
+ $context->delocalise();
+ delete $params->{ template };
+
+ if ($error) {
+# $error = $error->as_string if ref $error;
+ return $self->error($error);
+ }
+
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# context()
+#
+# Returns the internal CONTEXT reference.
+#------------------------------------------------------------------------
+
+sub context {
+ return $_[0]->{ CONTEXT };
+}
+
+
+#========================================================================
+# -- PRIVATE METHODS --
+#========================================================================
+
+sub _init {
+ my ($self, $config) = @_;
+ my ($item, $data, $context, $block, $blocks);
+ my $delim = $config->{ DELIMITER };
+ $delim = ':' unless defined $delim;
+
+ # coerce PRE_PROCESS, PROCESS and POST_PROCESS to arrays if necessary,
+ # by splitting on non-word characters
+ foreach $item (qw( PRE_PROCESS PROCESS POST_PROCESS WRAPPER )) {
+ $data = $config->{ $item };
+ $self->{ $item } = [ ], next unless (defined $data);
+ $data = [ split($delim, $data || '') ]
+ unless ref $data eq 'ARRAY';
+ $self->{ $item } = $data;
+ }
+ # unset PROCESS option unless explicitly specified in config
+ $self->{ PROCESS } = undef
+ unless defined $config->{ PROCESS };
+
+ $self->{ ERROR } = $config->{ ERROR } || $config->{ ERRORS };
+ $self->{ AUTO_RESET } = defined $config->{ AUTO_RESET }
+ ? $config->{ AUTO_RESET } : 1;
+ $self->{ DEBUG } = ( $config->{ DEBUG } || 0 )
+ & Template::Constants::DEBUG_SERVICE;
+
+ $context = $self->{ CONTEXT } = $config->{ CONTEXT }
+ || Template::Config->context($config)
+ || return $self->error(Template::Config->error);
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# _recover(\$exception)
+#
+# Examines the internal ERROR hash array to find a handler suitable
+# for the exception object passed by reference. Selecting the handler
+# is done by delegation to the exception's select_handler() method,
+# passing the set of handler keys as arguments. A 'default' handler
+# may also be provided. The handler value represents the name of a
+# template which should be processed.
+#------------------------------------------------------------------------
+
+sub _recover {
+ my ($self, $error) = @_;
+ my $context = $self->{ CONTEXT };
+ my ($hkey, $handler, $output);
+
+ # there shouldn't ever be a non-exception object received at this
+ # point... unless a module like CGI::Carp messes around with the
+ # DIE handler.
+ return undef
+ unless (ref $$error);
+
+ # a 'stop' exception is thrown by [% STOP %] - we return the output
+ # buffer stored in the exception object
+ return $$error->text()
+ if $$error->type() eq 'stop';
+
+ my $handlers = $self->{ ERROR }
+ || return undef; ## RETURN
+
+ if (ref $handlers eq 'HASH') {
+ if ($hkey = $$error->select_handler(keys %$handlers)) {
+ $handler = $handlers->{ $hkey };
+ $self->debug("using error handler for $hkey") if $self->{ DEBUG };
+ }
+ elsif ($handler = $handlers->{ default }) {
+ # use default handler
+ $self->debug("using default error handler") if $self->{ DEBUG };
+ }
+ else {
+ return undef; ## RETURN
+ }
+ }
+ else {
+ $handler = $handlers;
+ $self->debug("using default error handler") if $self->{ DEBUG };
+ }
+
+ eval { $handler = $context->template($handler) };
+ if ($@) {
+ $$error = $@;
+ return undef; ## RETURN
+ };
+
+ $context->stash->set('error', $$error);
+ eval {
+ $output .= $context->process($handler);
+ };
+ if ($@) {
+ $$error = $@;
+ return undef; ## RETURN
+ }
+
+ return $output;
+}
+
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which return a string representing the internal object
+# state.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $context = $self->{ CONTEXT }->_dump();
+ $context =~ s/\n/\n /gm;
+
+ my $error = $self->{ ERROR };
+ $error = join('',
+ "{\n",
+ (map { " $_ => $error->{ $_ }\n" }
+ keys %$error),
+ "}\n")
+ if ref $error;
+
+ local $" = ', ';
+ return <<EOF;
+$self
+PRE_PROCESS => [ @{ $self->{ PRE_PROCESS } } ]
+POST_PROCESS => [ @{ $self->{ POST_PROCESS } } ]
+ERROR => $error
+CONTEXT => $context
+EOF
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Service - General purpose template processing service
+
+=head1 SYNOPSIS
+
+ use Template::Service;
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => [ 'config', 'header' ],
+ POST_PROCESS => 'footer',
+ ERROR => {
+ user => 'user/index.html',
+ dbi => 'error/database',
+ default => 'error/default',
+ },
+ });
+
+ my $output = $service->process($template_name, \%replace)
+ || die $service->error(), "\n";
+
+=head1 DESCRIPTION
+
+The Template::Service module implements an object class for providing
+a consistent template processing service.
+
+Standard header (PRE_PROCESS) and footer (POST_PROCESS) templates may
+be specified which are prepended and appended to all templates
+processed by the service (but not any other templates or blocks
+INCLUDEd or PROCESSed from within). An ERROR hash may be specified
+which redirects the service to an alternate template file in the case
+of uncaught exceptions being thrown. This allows errors to be
+automatically handled by the service and a guaranteed valid response
+to be generated regardless of any processing problems encountered.
+
+A default Template::Service object is created by the Template module.
+Any Template::Service options may be passed to the Template new()
+constructor method and will be forwarded to the Template::Service
+constructor.
+
+ use Template;
+
+ my $template = Template->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+Similarly, the Template::Service constructor will forward all configuration
+parameters onto other default objects (e.g. Template::Context) that it may
+need to instantiate.
+
+A Template::Service object (or subclass/derivative) can be explicitly
+instantiated and passed to the Template new() constructor method as
+the SERVICE item.
+
+ use Template;
+ use Template::Service;
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+ my $template = Template->new({
+ SERVICE => $service,
+ });
+
+The Template::Service module can be sub-classed to create custom service
+handlers.
+
+ use Template;
+ use MyOrg::Template::Service;
+
+ my $service = MyOrg::Template::Service->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ COOL_OPTION => 'enabled in spades',
+ });
+
+ my $template = Template->new({
+ SERVICE => $service,
+ });
+
+The Template module uses the Template::Config service() factory method
+to create a default service object when required. The
+$Template::Config::SERVICE package variable may be set to specify an
+alternate service module. This will be loaded automatically and its
+new() constructor method called by the service() factory method when
+a default service object is required. Thus the previous example could
+be written as:
+
+ use Template;
+
+ $Template::Config::SERVICE = 'MyOrg::Template::Service';
+
+ my $template = Template->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ COOL_OPTION => 'enabled in spades',
+ });
+
+=head1 METHODS
+
+=head2 new(\%config)
+
+The new() constructor method is called to instantiate a Template::Service
+object. Configuration parameters may be specified as a HASH reference or
+as a list of (name =E<gt> value) pairs.
+
+ my $service1 = Template::Service->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ });
+
+ my $service2 = Template::Service->new( ERROR => 'error.html' );
+
+The new() method returns a Template::Service object (or sub-class) or
+undef on error. In the latter case, a relevant error message can be
+retrieved by the error() class method or directly from the
+$Template::Service::ERROR package variable.
+
+ my $service = Template::Service->new(\%config)
+ || die Template::Service->error();
+
+ my $service = Template::Service->new(\%config)
+ || die $Template::Service::ERROR;
+
+The following configuration items may be specified:
+
+=over 4
+
+
+
+
+=item PRE_PROCESS, POST_PROCESS
+
+These values may be set to contain the name(s) of template files
+(relative to INCLUDE_PATH) which should be processed immediately
+before and/or after each template. These do not get added to
+templates processed into a document via directives such as INCLUDE,
+PROCESS, WRAPPER etc.
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => 'header',
+ POST_PROCESS => 'footer',
+ };
+
+Multiple templates may be specified as a reference to a list. Each is
+processed in the order defined.
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => [ 'config', 'header' ],
+ POST_PROCESS => 'footer',
+ };
+
+Alternately, multiple template may be specified as a single string,
+delimited by ':'. This delimiter string can be changed via the
+DELIMITER option.
+
+ my $service = Template::Service->new({
+ PRE_PROCESS => 'config:header',
+ POST_PROCESS => 'footer',
+ };
+
+The PRE_PROCESS and POST_PROCESS templates are evaluated in the same
+variable context as the main document and may define or update
+variables for subsequent use.
+
+config:
+
+ [% # set some site-wide variables
+ bgcolor = '#ffffff'
+ version = 2.718
+ %]
+
+header:
+
+ [% DEFAULT title = 'My Funky Web Site' %]
+ <html>
+ <head>
+ <title>[% title %]</title>
+ </head>
+ <body bgcolor="[% bgcolor %]">
+
+footer:
+
+ <hr>
+ Version [% version %]
+ </body>
+ </html>
+
+The Template::Document object representing the main template being processed
+is available within PRE_PROCESS and POST_PROCESS templates as the 'template'
+variable. Metadata items defined via the META directive may be accessed
+accordingly.
+
+ $service->process('mydoc.html', $vars);
+
+mydoc.html:
+
+ [% META title = 'My Document Title' %]
+ blah blah blah
+ ...
+
+header:
+
+ <html>
+ <head>
+ <title>[% template.title %]</title></head>
+ <body bgcolor="[% bgcolor %]">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+=item PROCESS
+
+The PROCESS option may be set to contain the name(s) of template files
+(relative to INCLUDE_PATH) which should be processed instead of the
+main template passed to the Template::Service process() method. This can
+be used to apply consistent wrappers around all templates, similar to
+the use of PRE_PROCESS and POST_PROCESS templates.
+
+ my $service = Template::Service->new({
+ PROCESS => 'content',
+ };
+
+ # processes 'content' instead of 'foo.html'
+ $service->process('foo.html');
+
+A reference to the original template is available in the 'template'
+variable. Metadata items can be inspected and the template can be
+processed by specifying it as a variable reference (i.e. prefixed by
+'$') to an INCLUDE, PROCESS or WRAPPER directive.
+
+content:
+
+ <html>
+ <head>
+ <title>[% template.title %]</title>
+ </head>
+
+ <body>
+ [% PROCESS $template %]
+ <hr>
+ &copy; Copyright [% template.copyright %]
+ </body>
+ </html>
+
+foo.html:
+
+ [% META
+ title = 'The Foo Page'
+ author = 'Fred Foo'
+ copyright = '2000 Fred Foo'
+ %]
+ <h1>[% template.title %]</h1>
+ Welcome to the Foo Page, blah blah blah
+
+output:
+
+ <html>
+ <head>
+ <title>The Foo Page</title>
+ </head>
+
+ <body>
+ <h1>The Foo Page</h1>
+ Welcome to the Foo Page, blah blah blah
+ <hr>
+ &copy; Copyright 2000 Fred Foo
+ </body>
+ </html>
+
+
+
+
+
+
+
+=item ERROR
+
+The ERROR (or ERRORS if you prefer) configuration item can be used to
+name a single template or specify a hash array mapping exception types
+to templates which should be used for error handling. If an uncaught
+exception is raised from within a template then the appropriate error
+template will instead be processed.
+
+If specified as a single value then that template will be processed
+for all uncaught exceptions.
+
+ my $service = Template::Service->new({
+ ERROR => 'error.html'
+ });
+
+If the ERROR item is a hash reference the keys are assumed to be
+exception types and the relevant template for a given exception will
+be selected. A 'default' template may be provided for the general
+case. Note that 'ERROR' can be pluralised to 'ERRORS' if you find
+it more appropriate in this case.
+
+ my $service = Template::Service->new({
+ ERRORS => {
+ user => 'user/index.html',
+ dbi => 'error/database',
+ default => 'error/default',
+ },
+ });
+
+In this example, any 'user' exceptions thrown will cause the
+'user/index.html' template to be processed, 'dbi' errors are handled
+by 'error/database' and all others by the 'error/default' template.
+Any PRE_PROCESS and/or POST_PROCESS templates will also be applied
+to these error templates.
+
+Note that exception types are hierarchical and a 'foo' handler will
+catch all 'foo.*' errors (e.g. foo.bar, foo.bar.baz) if a more
+specific handler isn't defined. Be sure to quote any exception types
+that contain periods to prevent Perl concatenating them into a single
+string (i.e. C<user.passwd> is parsed as 'user'.'passwd').
+
+ my $service = Template::Service->new({
+ ERROR => {
+ 'user.login' => 'user/login.html',
+ 'user.passwd' => 'user/badpasswd.html',
+ 'user' => 'user/index.html',
+ 'default' => 'error/default',
+ },
+ });
+
+In this example, any template processed by the $service object, or
+other templates or code called from within, can raise a 'user.login'
+exception and have the service redirect to the 'user/login.html'
+template. Similarly, a 'user.passwd' exception has a specific
+handling template, 'user/badpasswd.html', while all other 'user' or
+'user.*' exceptions cause a redirection to the 'user/index.html' page.
+All other exception types are handled by 'error/default'.
+
+
+Exceptions can be raised in a template using the THROW directive,
+
+ [% THROW user.login 'no user id: please login' %]
+
+or by calling the throw() method on the current Template::Context object,
+
+ $context->throw('user.passwd', 'Incorrect Password');
+ $context->throw('Incorrect Password'); # type 'undef'
+
+or from Perl code by calling die() with a Template::Exception object,
+
+ die (Template::Exception->new('user.denied', 'Invalid User ID'));
+
+or by simply calling die() with an error string. This is
+automagically caught and converted to an exception of 'undef'
+type which can then be handled in the usual way.
+
+ die "I'm sorry Dave, I can't do that";
+
+
+
+
+
+
+
+=item AUTO_RESET
+
+The AUTO_RESET option is set by default and causes the local BLOCKS
+cache for the Template::Context object to be reset on each call to the
+Template process() method. This ensures that any BLOCKs defined
+within a template will only persist until that template is finished
+processing. This prevents BLOCKs defined in one processing request
+from interfering with other independent requests subsequently
+processed by the same context object.
+
+The BLOCKS item may be used to specify a default set of block definitions
+for the Template::Context object. Subsequent BLOCK definitions in templates
+will over-ride these but they will be reinstated on each reset if AUTO_RESET
+is enabled (default), or if the Template::Context reset() method is called.
+
+
+
+
+
+
+
+=item DEBUG
+
+The DEBUG option can be used to enable debugging messages from the
+Template::Service module by setting it to include the DEBUG_SERVICE
+value.
+
+ use Template::Constants qw( :debug );
+
+ my $template = Template->new({
+ DEBUG => DEBUG_SERVICE,
+ });
+
+
+
+
+=back
+
+=head2 process($input, \%replace)
+
+The process() method is called to process a template specified as the first
+parameter, $input. This may be a file name, file handle (e.g. GLOB or IO::Handle)
+or a reference to a text string containing the template text. An additional
+hash reference may be passed containing template variable definitions.
+
+The method processes the template, adding any PRE_PROCESS or POST_PROCESS
+templates defined, and returns the output text. An uncaught exception thrown
+by the template will be handled by a relevant ERROR handler if defined.
+Errors that occur in the PRE_PROCESS or POST_PROCESS templates, or those that
+occur in the main input template and aren't handled, cause the method to
+return undef to indicate failure. The appropriate error message can be
+retrieved via the error() method.
+
+ $service->process('myfile.html', { title => 'My Test File' })
+ || die $service->error();
+
+
+=head2 context()
+
+Returns a reference to the internal context object which is, by default, an
+instance of the Template::Context class.
+
+=head2 error()
+
+Returns the most recent error message.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.70, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Context|Template::Context>
diff --git a/lib/Template/Stash.pm b/lib/Template/Stash.pm
new file mode 100644
index 0000000..4f26bca
--- /dev/null
+++ b/lib/Template/Stash.pm
@@ -0,0 +1,1000 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Stash
+#
+# DESCRIPTION
+# Definition of an object class which stores and manages access to
+# variables for the Template Toolkit.
+#
+# AUTHOR
+# Andy Wardley <abw@wardley.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Stash.pm,v 2.78 2003/07/24 12:13:32 abw Exp $
+#
+#============================================================================
+
+package Template::Stash;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $ROOT_OPS $SCALAR_OPS $HASH_OPS $LIST_OPS );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.78 $ =~ /(\d+)\.(\d+)/);
+
+
+#========================================================================
+# -- PACKAGE VARIABLES AND SUBS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# Definitions of various pseudo-methods. ROOT_OPS are merged into all
+# new Template::Stash objects, and are thus default global functions.
+# SCALAR_OPS are methods that can be called on a scalar, and ditto
+# respectively for LIST_OPS and HASH_OPS
+#------------------------------------------------------------------------
+
+$ROOT_OPS = {
+ 'inc' => sub { local $^W = 0; my $item = shift; ++$item },
+ 'dec' => sub { local $^W = 0; my $item = shift; --$item },
+# import => \&hash_import,
+ defined $ROOT_OPS ? %$ROOT_OPS : (),
+};
+
+$SCALAR_OPS = {
+ 'item' => sub { $_[0] },
+ 'list' => sub { [ $_[0] ] },
+ 'hash' => sub { { value => $_[0] } },
+ 'length' => sub { length $_[0] },
+ 'size' => sub { return 1 },
+ 'defined' => sub { return 1 },
+ 'repeat' => sub {
+ my ($str, $count) = @_;
+ $str = '' unless defined $str;
+ $count ||= 1;
+ return $str x $count;
+ },
+ 'search' => sub {
+ my ($str, $pattern) = @_;
+ return $str unless defined $str and defined $pattern;
+ return $str =~ /$pattern/;
+ },
+ 'replace' => sub {
+ my ($str, $search, $replace) = @_;
+ $replace = '' unless defined $replace;
+ return $str unless defined $str and defined $search;
+ $str =~ s/$search/$replace/g;
+# print STDERR "s [ $search ] [ $replace ] g\n";
+# eval "\$str =~ s$search$replaceg";
+ return $str;
+ },
+ 'match' => sub {
+ my ($str, $search) = @_;
+ return $str unless defined $str and defined $search;
+ my @matches = ($str =~ /$search/);
+ return @matches ? \@matches : '';
+ },
+ 'split' => sub {
+ my ($str, $split, @args) = @_;
+ $str = '' unless defined $str;
+ return [ defined $split ? split($split, $str, @args)
+ : split(' ', $str, @args) ];
+ },
+ 'chunk' => sub {
+ my ($string, $size) = @_;
+ my @list;
+ $size ||= 1;
+ if ($size < 0) {
+ # sexeger! It's faster to reverse the string, search
+ # it from the front and then reverse the output than to
+ # search it from the end, believe it nor not!
+ $string = reverse $string;
+ $size = -$size;
+ unshift(@list, scalar reverse $1)
+ while ($string =~ /((.{$size})|(.+))/g);
+ }
+ else {
+ push(@list, $1) while ($string =~ /((.{$size})|(.+))/g);
+ }
+ return \@list;
+ },
+
+
+ defined $SCALAR_OPS ? %$SCALAR_OPS : (),
+};
+
+$HASH_OPS = {
+ 'item' => sub { my ($hash, $item) = @_;
+ $item = '' unless defined $item;
+ $hash->{ $item };
+ },
+ 'hash' => sub { $_[0] },
+ 'size' => sub { scalar keys %{$_[0]} },
+ 'keys' => sub { [ keys %{ $_[0] } ] },
+ 'values' => sub { [ values %{ $_[0] } ] },
+ 'each' => sub { [ %{ $_[0] } ] },
+ 'list' => sub { my ($hash, $what) = @_; $what ||= '';
+ return ($what eq 'keys') ? [ keys %$hash ]
+ : ($what eq 'values') ? [ values %$hash ]
+ : ($what eq 'each') ? [ %$hash ]
+ : [ map { { key => $_ , value => $hash->{ $_ } } }
+ keys %$hash ];
+ },
+ 'exists' => sub { exists $_[0]->{ $_[1] } },
+ 'defined' => sub { defined $_[0]->{ $_[1] } },
+ 'import' => \&hash_import,
+ 'sort' => sub {
+ my ($hash) = @_;
+ [ sort { lc $hash->{$a} cmp lc $hash->{$b} } (keys %$hash) ];
+ },
+ 'nsort' => sub {
+ my ($hash) = @_;
+ [ sort { $hash->{$a} <=> $hash->{$b} } (keys %$hash) ];
+ },
+ defined $HASH_OPS ? %$HASH_OPS : (),
+};
+
+$LIST_OPS = {
+ 'item' => sub { $_[0]->[ $_[1] || 0 ] },
+ 'list' => sub { $_[0] },
+ 'hash' => sub { my $list = shift; my $n = 0;
+ return { map { ($n++, $_) } @$list }; },
+ 'push' => sub { my $list = shift; push(@$list, shift); return '' },
+ 'pop' => sub { my $list = shift; pop(@$list) },
+ 'unshift' => sub { my $list = shift; unshift(@$list, shift); return '' },
+ 'shift' => sub { my $list = shift; shift(@$list) },
+ 'max' => sub { local $^W = 0; my $list = shift; $#$list; },
+ 'size' => sub { local $^W = 0; my $list = shift; $#$list + 1; },
+ 'first' => sub {
+ my $list = shift;
+ return $list->[0] unless @_;
+ return [ @$list[0..$_[0]-1] ];
+ },
+ 'last' => sub {
+ my $list = shift;
+ return $list->[-1] unless @_;
+ return [ @$list[-$_[0]..-1] ];
+ },
+ 'reverse' => sub { my $list = shift; [ reverse @$list ] },
+ 'grep' => sub {
+ my ($list, $pattern) = @_;
+ $pattern ||= '';
+ return [ grep /$pattern/, @$list ];
+ },
+ 'join' => sub {
+ my ($list, $joint) = @_;
+ join(defined $joint ? $joint : ' ',
+ map { defined $_ ? $_ : '' } @$list)
+ },
+ 'sort' => sub {
+ $^W = 0;
+ my ($list, $field) = @_;
+ return $list unless @$list > 1; # no need to sort 1 item lists
+ return $field # Schwartzian Transform
+ ? map { $_->[0] } # for case insensitivity
+ sort { $a->[1] cmp $b->[1] }
+ map { [ $_, lc(ref($_) eq 'HASH'
+ ? $_->{ $field } :
+ UNIVERSAL::can($_, $field)
+ ? $_->$field() : $_) ] }
+ @$list
+ : map { $_->[0] }
+ sort { $a->[1] cmp $b->[1] }
+ map { [ $_, lc $_ ] }
+ @$list
+ },
+ 'nsort' => sub {
+ my ($list, $field) = @_;
+ return $list unless $#$list; # no need to sort 1 item lists
+ return $field # Schwartzian Transform
+ ? map { $_->[0] } # for case insensitivity
+ sort { $a->[1] <=> $b->[1] }
+ map { [ $_, lc(ref($_) eq 'HASH'
+ ? $_->{ $field } :
+ UNIVERSAL::can($_, $field)
+ ? $_->$field() : $_) ] }
+ @$list
+ : map { $_->[0] }
+ sort { $a->[1] <=> $b->[1] }
+ map { [ $_, lc $_ ] }
+ @$list
+ },
+ 'unique' => sub { my %u; [ grep { ++$u{$_} == 1 } @{$_[0]} ] },
+ 'merge' => sub {
+ my $list = shift;
+ return [ @$list, grep defined, map ref eq 'ARRAY' ? @$_ : undef, @_ ];
+ },
+ 'slice' => sub {
+ my ($list, $from, $to) = @_;
+ $from ||= 0;
+ $to = $#$list unless defined $to;
+ return [ @$list[$from..$to] ];
+ },
+ 'splice' => sub {
+ my ($list, $offset, $length, @replace) = @_;
+
+ if (@replace) {
+ # @replace can contain a list of multiple replace items, or
+ # be a single reference to a list
+ @replace = @{ $replace[0] }
+ if @replace == 1 && ref $replace[0] eq 'ARRAY';
+ return [ splice @$list, $offset, $length, @replace ];
+ }
+ elsif (defined $length) {
+ return [ splice @$list, $offset, $length ];
+ }
+ elsif (defined $offset) {
+ return [ splice @$list, $offset ];
+ }
+ else {
+ return [ splice(@$list) ];
+ }
+ },
+
+ defined $LIST_OPS ? %$LIST_OPS : (),
+};
+
+sub hash_import {
+ my ($hash, $imp) = @_;
+ $imp = {} unless ref $imp eq 'HASH';
+ @$hash{ keys %$imp } = values %$imp;
+ return '';
+}
+
+
+#------------------------------------------------------------------------
+# define_vmethod($type, $name, \&sub)
+#
+# Defines a virtual method of type $type (SCALAR, HASH, or LIST), with
+# name $name, that invokes &sub when called. It is expected that &sub
+# be able to handle the type that it will be called upon.
+#------------------------------------------------------------------------
+
+sub define_vmethod {
+ my ($class, $type, $name, $sub) = @_;
+ my $op;
+ $type = lc $type;
+
+ if ($type =~ /^scalar|item$/) {
+ $op = $SCALAR_OPS;
+ }
+ elsif ($type eq 'hash') {
+ $op = $HASH_OPS;
+ }
+ elsif ($type =~ /^list|array$/) {
+ $op = $LIST_OPS;
+ }
+ else {
+ die "invalid vmethod type: $type\n";
+ }
+
+ $op->{ $name } = $sub;
+
+ return 1;
+}
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\%params)
+#
+# Constructor method which creates a new Template::Stash object.
+# An optional hash reference may be passed containing variable
+# definitions that will be used to initialise the stash.
+#
+# Returns a reference to a newly created Template::Stash.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $params = ref $_[0] eq 'HASH' ? shift(@_) : { @_ };
+
+ my $self = {
+ global => { },
+ %$params,
+ %$ROOT_OPS,
+ '_PARENT' => undef,
+ };
+
+ bless $self, $class;
+}
+
+
+#========================================================================
+# ----- PUBLIC OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# clone(\%params)
+#
+# Creates a copy of the current stash object to effect localisation
+# of variables. The new stash is blessed into the same class as the
+# parent (which may be a derived class) and has a '_PARENT' member added
+# which contains a reference to the parent stash that created it
+# ($self). This member is used in a successive declone() method call to
+# return the reference to the parent.
+#
+# A parameter may be provided which should reference a hash of
+# variable/values which should be defined in the new stash. The
+# update() method is called to define these new variables in the cloned
+# stash.
+#
+# Returns a reference to a cloned Template::Stash.
+#------------------------------------------------------------------------
+
+sub clone {
+ my ($self, $params) = @_;
+ $params ||= { };
+
+ # look out for magical 'import' argument which imports another hash
+ my $import = $params->{ import };
+ if (defined $import && UNIVERSAL::isa($import, 'HASH')) {
+ delete $params->{ import };
+ }
+ else {
+ undef $import;
+ }
+
+ my $clone = bless {
+ %$self, # copy all parent members
+ %$params, # copy all new data
+ '_PARENT' => $self, # link to parent
+ }, ref $self;
+
+ # perform hash import if defined
+ &{ $HASH_OPS->{ import }}($clone, $import)
+ if defined $import;
+
+ return $clone;
+}
+
+
+#------------------------------------------------------------------------
+# declone($export)
+#
+# Returns a reference to the PARENT stash. When called in the following
+# manner:
+# $stash = $stash->declone();
+# the reference count on the current stash will drop to 0 and be "freed"
+# and the caller will be left with a reference to the parent. This
+# contains the state of the stash before it was cloned.
+#------------------------------------------------------------------------
+
+sub declone {
+ my $self = shift;
+ $self->{ _PARENT } || $self;
+}
+
+
+#------------------------------------------------------------------------
+# get($ident)
+#
+# Returns the value for an variable stored in the stash. The variable
+# may be specified as a simple string, e.g. 'foo', or as an array
+# reference representing compound variables. In the latter case, each
+# pair of successive elements in the list represent a node in the
+# compound variable. The first is the variable name, the second a
+# list reference of arguments or 0 if undefined. So, the compound
+# variable [% foo.bar('foo').baz %] would be represented as the list
+# [ 'foo', 0, 'bar', ['foo'], 'baz', 0 ]. Returns the value of the
+# identifier or an empty string if undefined. Errors are thrown via
+# die().
+#------------------------------------------------------------------------
+
+sub get {
+ my ($self, $ident, $args) = @_;
+ my ($root, $result);
+ $root = $self;
+
+ if (ref $ident eq 'ARRAY'
+ || ($ident =~ /\./)
+ && ($ident = [ map { s/\(.*$//; ($_, 0) } split(/\./, $ident) ])) {
+ my $size = $#$ident;
+
+ # if $ident is a list reference, then we evaluate each item in the
+ # identifier against the previous result, using the root stash
+ # ($self) as the first implicit 'result'...
+
+ foreach (my $i = 0; $i <= $size; $i += 2) {
+ $result = $self->_dotop($root, @$ident[$i, $i+1]);
+ last unless defined $result;
+ $root = $result;
+ }
+ }
+ else {
+ $result = $self->_dotop($root, $ident, $args);
+ }
+
+ return defined $result ? $result : $self->undefined($ident, $args);
+}
+
+
+#------------------------------------------------------------------------
+# set($ident, $value, $default)
+#
+# Updates the value for a variable in the stash. The first parameter
+# should be the variable name or array, as per get(). The second
+# parameter should be the intended value for the variable. The third,
+# optional parameter is a flag which may be set to indicate 'default'
+# mode. When set true, the variable will only be updated if it is
+# currently undefined or has a false value. The magical 'IMPORT'
+# variable identifier may be used to indicate that $value is a hash
+# reference whose values should be imported. Returns the value set,
+# or an empty string if not set (e.g. default mode). In the case of
+# IMPORT, returns the number of items imported from the hash.
+#------------------------------------------------------------------------
+
+sub set {
+ my ($self, $ident, $value, $default) = @_;
+ my ($root, $result, $error);
+
+ $root = $self;
+
+ ELEMENT: {
+ if (ref $ident eq 'ARRAY'
+ || ($ident =~ /\./)
+ && ($ident = [ map { s/\(.*$//; ($_, 0) }
+ split(/\./, $ident) ])) {
+
+ # a compound identifier may contain multiple elements (e.g.
+ # foo.bar.baz) and we must first resolve all but the last,
+ # using _dotop() with the $lvalue flag set which will create
+ # intermediate hashes if necessary...
+ my $size = $#$ident;
+ foreach (my $i = 0; $i < $size - 2; $i += 2) {
+ $result = $self->_dotop($root, @$ident[$i, $i+1], 1);
+ last ELEMENT unless defined $result;
+ $root = $result;
+ }
+
+ # then we call _assign() to assign the value to the last element
+ $result = $self->_assign($root, @$ident[$size-1, $size],
+ $value, $default);
+ }
+ else {
+ $result = $self->_assign($root, $ident, 0, $value, $default);
+ }
+ }
+
+ return defined $result ? $result : '';
+}
+
+
+#------------------------------------------------------------------------
+# getref($ident)
+#
+# Returns a "reference" to a particular item. This is represented as a
+# closure which will return the actual stash item when called.
+# WARNING: still experimental!
+#------------------------------------------------------------------------
+
+sub getref {
+ my ($self, $ident, $args) = @_;
+ my ($root, $item, $result);
+ $root = $self;
+
+ if (ref $ident eq 'ARRAY') {
+ my $size = $#$ident;
+
+ foreach (my $i = 0; $i <= $size; $i += 2) {
+ ($item, $args) = @$ident[$i, $i + 1];
+ last if $i >= $size - 2; # don't evaluate last node
+ last unless defined
+ ($root = $self->_dotop($root, $item, $args));
+ }
+ }
+ else {
+ $item = $ident;
+ }
+
+ if (defined $root) {
+ return sub { my @args = (@{$args||[]}, @_);
+ $self->_dotop($root, $item, \@args);
+ }
+ }
+ else {
+ return sub { '' };
+ }
+}
+
+
+
+
+#------------------------------------------------------------------------
+# update(\%params)
+#
+# Update multiple variables en masse. No magic is performed. Simple
+# variable names only.
+#------------------------------------------------------------------------
+
+sub update {
+ my ($self, $params) = @_;
+
+ # look out for magical 'import' argument to import another hash
+ my $import = $params->{ import };
+ if (defined $import && UNIVERSAL::isa($import, 'HASH')) {
+ @$self{ keys %$import } = values %$import;
+ delete $params->{ import };
+ }
+
+ @$self{ keys %$params } = values %$params;
+}
+
+
+#------------------------------------------------------------------------
+# undefined($ident, $args)
+#
+# Method called when a get() returns an undefined value. Can be redefined
+# in a subclass to implement alternate handling.
+#------------------------------------------------------------------------
+
+sub undefined {
+ my ($self, $ident, $args);
+ return '';
+}
+
+
+#========================================================================
+# ----- PRIVATE OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _dotop($root, $item, \@args, $lvalue)
+#
+# This is the core 'dot' operation method which evaluates elements of
+# variables against their root. All variables have an implicit root
+# which is the stash object itself (a hash). Thus, a non-compound
+# variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is
+# '(stash.)foo.bar'. The first parameter is a reference to the current
+# root, initially the stash itself. The second parameter contains the
+# name of the variable element, e.g. 'foo'. The third optional
+# parameter is a reference to a list of any parenthesised arguments
+# specified for the variable, which are passed to sub-routines, object
+# methods, etc. The final parameter is an optional flag to indicate
+# if this variable is being evaluated on the left side of an assignment
+# (e.g. foo.bar.baz = 10). When set true, intermediated hashes will
+# be created (e.g. bar) if necessary.
+#
+# Returns the result of evaluating the item against the root, having
+# performed any variable "magic". The value returned can then be used
+# as the root of the next _dotop() in a compound sequence. Returns
+# undef if the variable is undefined.
+#------------------------------------------------------------------------
+
+sub _dotop {
+ my ($self, $root, $item, $args, $lvalue) = @_;
+ my $rootref = ref $root;
+ my $atroot = ($root eq $self);
+ my ($value, @result);
+
+ $args ||= [ ];
+ $lvalue ||= 0;
+
+# print STDERR "_dotop(root=$root, item=$item, args=[@$args])\n"
+# if $DEBUG;
+
+ # return undef without an error if either side of the dot is unviable
+ # or if an attempt is made to access a private member, starting _ or .
+ return undef
+ unless defined($root) and defined($item) and $item !~ /^[\._]/;
+
+ if ($atroot || $rootref eq 'HASH') {
+
+ # if $root is a regular HASH or a Template::Stash kinda HASH (the
+ # *real* root of everything). We first lookup the named key
+ # in the hash, or create an empty hash in its place if undefined
+ # and the $lvalue flag is set. Otherwise, we check the HASH_OPS
+ # pseudo-methods table, calling the code if found, or return undef.
+
+ if (defined($value = $root->{ $item })) {
+ return $value unless ref $value eq 'CODE'; ## RETURN
+ @result = &$value(@$args); ## @result
+ }
+ elsif ($lvalue) {
+ # we create an intermediate hash if this is an lvalue
+ return $root->{ $item } = { }; ## RETURN
+ }
+ # ugly hack: only allow import vmeth to be called on root stash
+ elsif (($value = $HASH_OPS->{ $item })
+ && ! $atroot || $item eq 'import') {
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif ( ref $item eq 'ARRAY' ) {
+ # hash slice
+ return [@$root{@$item}]; ## RETURN
+ }
+ }
+ elsif ($rootref eq 'ARRAY') {
+
+ # if root is an ARRAY then we check for a LIST_OPS pseudo-method
+ # (except for l-values for which it doesn't make any sense)
+ # or return the numerical index into the array, or undef
+
+ if (($value = $LIST_OPS->{ $item }) && ! $lvalue) {
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif ($item =~ /^-?\d+$/) {
+ $value = $root->[$item];
+ return $value unless ref $value eq 'CODE'; ## RETURN
+ @result = &$value(@$args); ## @result
+ }
+ elsif ( ref $item eq 'ARRAY' ) {
+ # array slice
+ return [@$root[@$item]]; ## RETURN
+ }
+ }
+
+ # NOTE: we do the can-can because UNIVSERAL::isa($something, 'UNIVERSAL')
+ # doesn't appear to work with CGI, returning true for the first call
+ # and false for all subsequent calls.
+
+ elsif (ref($root) && UNIVERSAL::can($root, 'can')) {
+
+ # if $root is a blessed reference (i.e. inherits from the
+ # UNIVERSAL object base class) then we call the item as a method.
+ # If that fails then we try to fallback on HASH behaviour if
+ # possible.
+ eval { @result = $root->$item(@$args); };
+
+ if ($@) {
+ # temporary hack - required to propogate errors thrown
+ # by views; if $@ is a ref (e.g. Template::Exception
+ # object then we assume it's a real error that needs
+ # real throwing
+
+ die $@ if ref($@) || ($@ !~ /Can't locate object method/);
+
+ # failed to call object method, so try some fallbacks
+ if (UNIVERSAL::isa($root, 'HASH')
+ && defined($value = $root->{ $item })) {
+ return $value unless ref $value eq 'CODE'; ## RETURN
+ @result = &$value(@$args);
+ }
+ elsif (UNIVERSAL::isa($root, 'ARRAY')
+ && ($value = $LIST_OPS->{ $item })) {
+ @result = &$value($root, @$args);
+ }
+ elsif ($value = $SCALAR_OPS->{ $item }) {
+ @result = &$value($root, @$args);
+ }
+ elsif ($value = $LIST_OPS->{ $item }) {
+ @result = &$value([$root], @$args);
+ }
+ elsif ($self->{ _DEBUG }) {
+ @result = (undef, $@);
+ }
+ }
+ }
+ elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) {
+ # at this point, it doesn't look like we've got a reference to
+ # anything we know about, so we try the SCALAR_OPS pseudo-methods
+ # table (but not for l-values)
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif (($value = $LIST_OPS->{ $item }) && ! $lvalue) {
+ # last-ditch: can we promote a scalar to a one-element
+ # list and apply a LIST_OPS virtual method?
+ @result = &$value([$root], @$args);
+ }
+ elsif ($self->{ _DEBUG }) {
+ die "don't know how to access [ $root ].$item\n"; ## DIE
+ }
+ else {
+ @result = ();
+ }
+
+ # fold multiple return items into a list unless first item is undef
+ if (defined $result[0]) {
+ return ## RETURN
+ scalar @result > 1 ? [ @result ] : $result[0];
+ }
+ elsif (defined $result[1]) {
+ die $result[1]; ## DIE
+ }
+ elsif ($self->{ _DEBUG }) {
+ die "$item is undefined\n"; ## DIE
+ }
+
+ return undef;
+}
+
+
+#------------------------------------------------------------------------
+# _assign($root, $item, \@args, $value, $default)
+#
+# Similar to _dotop() above, but assigns a value to the given variable
+# instead of simply returning it. The first three parameters are the
+# root item, the item and arguments, as per _dotop(), followed by the
+# value to which the variable should be set and an optional $default
+# flag. If set true, the variable will only be set if currently false
+# (undefined/zero)
+#------------------------------------------------------------------------
+
+sub _assign {
+ my ($self, $root, $item, $args, $value, $default) = @_;
+ my $rootref = ref $root;
+ my $atroot = ($root eq $self);
+ my $result;
+ $args ||= [ ];
+ $default ||= 0;
+
+# print(STDERR "_assign(root=$root, item=$item, args=[@$args], \n",
+# "value=$value, default=$default)\n")
+# if $DEBUG;
+
+ # return undef without an error if either side of the dot is unviable
+ # or if an attempt is made to update a private member, starting _ or .
+ return undef ## RETURN
+ unless $root and defined $item and $item !~ /^[\._]/;
+
+ if ($rootref eq 'HASH' || $atroot) {
+# if ($item eq 'IMPORT' && UNIVERSAL::isa($value, 'HASH')) {
+# # import hash entries into root hash
+# @$root{ keys %$value } = values %$value;
+# return ''; ## RETURN
+# }
+ # if the root is a hash we set the named key
+ return ($root->{ $item } = $value) ## RETURN
+ unless $default && $root->{ $item };
+ }
+ elsif ($rootref eq 'ARRAY' && $item =~ /^-?\d+$/) {
+ # or set a list item by index number
+ return ($root->[$item] = $value) ## RETURN
+ unless $default && $root->{ $item };
+ }
+ elsif (UNIVERSAL::isa($root, 'UNIVERSAL')) {
+ # try to call the item as a method of an object
+
+ return $root->$item(@$args, $value) ## RETURN
+ unless $default && $root->$item();
+
+# 2 issues:
+# - method call should be wrapped in eval { }
+# - fallback on hash methods if object method not found
+#
+# eval { $result = $root->$item(@$args, $value); };
+#
+# if ($@) {
+# die $@ if ref($@) || ($@ !~ /Can't locate object method/);
+#
+# # failed to call object method, so try some fallbacks
+# if (UNIVERSAL::isa($root, 'HASH') && exists $root->{ $item }) {
+# $result = ($root->{ $item } = $value)
+# unless $default && $root->{ $item };
+# }
+# }
+# return $result; ## RETURN
+
+ }
+ else {
+ die "don't know how to assign to [$root].[$item]\n"; ## DIE
+ }
+
+ return undef;
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal state
+# of the object. The method calls itself recursively to dump sub-hashes.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ return "[Template::Stash] " . $self->_dump_frame(2);
+}
+
+sub _dump_frame {
+ my ($self, $indent) = @_;
+ $indent ||= 1;
+ my $buffer = ' ';
+ my $pad = $buffer x $indent;
+ my $text = "{\n";
+ local $" = ', ';
+
+ my ($key, $value);
+
+ return $text . "...excessive recursion, terminating\n"
+ if $indent > 32;
+
+ foreach $key (keys %$self) {
+ $value = $self->{ $key };
+ $value = '<undef>' unless defined $value;
+ next if $key =~ /^\./;
+ if (ref($value) eq 'ARRAY') {
+ $value = '[ ' . join(', ', map { defined $_ ? $_ : '<undef>' }
+ @$value) . ' ]';
+ }
+ elsif (ref $value eq 'HASH') {
+ $value = _dump_frame($value, $indent + 1);
+ }
+
+ $text .= sprintf("$pad%-16s => $value\n", $key);
+ }
+ $text .= $buffer x ($indent - 1) . '}';
+ return $text;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Stash - Magical storage for template variables
+
+=head1 SYNOPSIS
+
+ use Template::Stash;
+
+ my $stash = Template::Stash->new(\%vars);
+
+ # get variable values
+ $value = $stash->get($variable);
+ $value = $stash->get(\@compound);
+
+ # set variable value
+ $stash->set($variable, $value);
+ $stash->set(\@compound, $value);
+
+ # default variable value
+ $stash->set($variable, $value, 1);
+ $stash->set(\@compound, $value, 1);
+
+ # set variable values en masse
+ $stash->update(\%new_vars)
+
+ # methods for (de-)localising variables
+ $stash = $stash->clone(\%new_vars);
+ $stash = $stash->declone();
+
+=head1 DESCRIPTION
+
+The Template::Stash module defines an object class which is used to store
+variable values for the runtime use of the template processor. Variable
+values are stored internally in a hash reference (which itself is blessed
+to create the object) and are accessible via the get() and set() methods.
+
+Variables may reference hash arrays, lists, subroutines and objects
+as well as simple values. The stash automatically performs the right
+magic when dealing with variables, calling code or object methods,
+indexing into lists, hashes, etc.
+
+The stash has clone() and declone() methods which are used by the
+template processor to make temporary copies of the stash for
+localising changes made to variables.
+
+=head1 PUBLIC METHODS
+
+=head2 new(\%params)
+
+The new() constructor method creates and returns a reference to a new
+Template::Stash object.
+
+ my $stash = Template::Stash->new();
+
+A hash reference may be passed to provide variables and values which
+should be used to initialise the stash.
+
+ my $stash = Template::Stash->new({ var1 => 'value1',
+ var2 => 'value2' });
+
+=head2 get($variable)
+
+The get() method retrieves the variable named by the first parameter.
+
+ $value = $stash->get('var1');
+
+Dotted compound variables can be retrieved by specifying the variable
+elements by reference to a list. Each node in the variable occupies
+two entries in the list. The first gives the name of the variable
+element, the second is a reference to a list of arguments for that
+element, or 0 if none.
+
+ [% foo.bar(10).baz(20) %]
+
+ $stash->get([ 'foo', 0, 'bar', [ 10 ], 'baz', [ 20 ] ]);
+
+=head2 set($variable, $value, $default)
+
+The set() method sets the variable name in the first parameter to the
+value specified in the second.
+
+ $stash->set('var1', 'value1');
+
+If the third parameter evaluates to a true value, the variable is
+set only if it did not have a true value before.
+
+ $stash->set('var2', 'default_value', 1);
+
+Dotted compound variables may be specified as per get() above.
+
+ [% foo.bar = 30 %]
+
+ $stash->set([ 'foo', 0, 'bar', 0 ], 30);
+
+The magical variable 'IMPORT' can be specified whose corresponding
+value should be a hash reference. The contents of the hash array are
+copied (i.e. imported) into the current namespace.
+
+ # foo.bar = baz, foo.wiz = waz
+ $stash->set('foo', { 'bar' => 'baz', 'wiz' => 'waz' });
+
+ # import 'foo' into main namespace: foo = baz, wiz = waz
+ $stash->set('IMPORT', $stash->get('foo'));
+
+=head2 clone(\%params)
+
+The clone() method creates and returns a new Template::Stash object which
+represents a localised copy of the parent stash. Variables can be
+freely updated in the cloned stash and when declone() is called, the
+original stash is returned with all its members intact and in the
+same state as they were before clone() was called.
+
+For convenience, a hash of parameters may be passed into clone() which
+is used to update any simple variable (i.e. those that don't contain any
+namespace elements like 'foo' and 'bar' but not 'foo.bar') variables while
+cloning the stash. For adding and updating complex variables, the set()
+method should be used after calling clone(). This will correctly resolve
+and/or create any necessary namespace hashes.
+
+A cloned stash maintains a reference to the stash that it was copied
+from in its '_PARENT' member.
+
+=head2 declone()
+
+The declone() method returns the '_PARENT' reference and can be used to
+restore the state of a stash as described above.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.78, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>, L<Template::Context|Template::Context>
diff --git a/lib/Template/Stash/Context.pm b/lib/Template/Stash/Context.pm
new file mode 100644
index 0000000..8f9cfdb
--- /dev/null
+++ b/lib/Template/Stash/Context.pm
@@ -0,0 +1,781 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Stash::Context
+#
+# DESCRIPTION
+# This is an alternate stash object which includes a patch from
+# Craig Barratt to implement various new virtual methods to allow
+# dotted template variable to denote if object methods and subroutines
+# should be called in scalar or list context. It adds a little overhead
+# to each stash call and I'm a little wary of doing that. So for now,
+# it's implemented as a separate stash module which will allow us to
+# test it out, benchmark it and switch it in or out as we require.
+#
+# This is what Craig has to say about it:
+#
+# Here's a better set of features for the core. Attached is a new version
+# of Stash.pm (based on TT2.02) that:
+#
+# - supports the special op "scalar" that forces scalar context on
+# function calls, eg:
+#
+# cgi.param("foo").scalar
+#
+# calls cgi.param("foo") in scalar context (unlike my wimpy
+# scalar op from last night). Array context is the default.
+#
+# With non-function operands, scalar behaves like the perl
+# version (eg: no-op for scalar, size for arrays, etc).
+#
+# - supports the special op "ref" that behaves like the perl ref.
+# If applied to a function the function is not called. Eg:
+#
+# cgi.param("foo").ref
+#
+# does *not* call cgi.param and evaluates to "CODE". Similarly,
+# HASH.ref, ARRAY.ref return what you expect.
+#
+# - adds a new scalar and list op called "array" that is a no-op for
+# arrays and promotes scalars to one-element arrays.
+#
+# - allows scalar ops to be applied to arrays and hashes in place,
+# eg: ARRAY.repeat(3) repeats each element in place.
+#
+# - allows list ops to be applied to scalars by promoting the scalars
+# to one-element arrays (like an implicit "array"). So you can
+# do things like SCALAR.size, SCALAR.join and get a useful result.
+#
+# This also means you can now use x.0 to safely get the first element
+# whether x is an array or scalar.
+#
+# The new Stash.pm passes the TT2.02 test suite. But I haven't tested the
+# new features very much. One nagging implementation problem is that the
+# "scalar" and "ref" ops have higher precedence than user variable names.
+#
+# AUTHORS
+# Andy Wardley <abw@kfs.org>
+# Craig Barratt <craig@arraycomm.com>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2001 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2001 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Context.pm,v 1.53 2003/04/24 09:14:47 abw Exp $
+#
+#============================================================================
+
+package Template::Stash::Context;
+
+require 5.004;
+
+use strict;
+use Template::Stash;
+use vars qw( $VERSION $DEBUG $ROOT_OPS $SCALAR_OPS $HASH_OPS $LIST_OPS );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 1.53 $ =~ /(\d+)\.(\d+)/);
+
+
+#========================================================================
+# -- PACKAGE VARIABLES AND SUBS --
+#========================================================================
+
+#------------------------------------------------------------------------
+# copy virtual methods from those in the regular Template::Stash
+#------------------------------------------------------------------------
+
+$ROOT_OPS = {
+ %$Template::Stash::ROOT_OPS,
+ defined $ROOT_OPS ? %$ROOT_OPS : (),
+};
+
+$SCALAR_OPS = {
+ %$Template::Stash::SCALAR_OPS,
+ 'array' => sub { return [$_[0]] },
+ defined $SCALAR_OPS ? %$SCALAR_OPS : (),
+};
+
+$LIST_OPS = {
+ %$Template::Stash::LIST_OPS,
+ 'array' => sub { return $_[0] },
+ defined $LIST_OPS ? %$LIST_OPS : (),
+};
+
+$HASH_OPS = {
+ %$Template::Stash::HASH_OPS,
+ defined $HASH_OPS ? %$HASH_OPS : (),
+};
+
+
+
+#========================================================================
+# ----- CLASS METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# new(\%params)
+#
+# Constructor method which creates a new Template::Stash object.
+# An optional hash reference may be passed containing variable
+# definitions that will be used to initialise the stash.
+#
+# Returns a reference to a newly created Template::Stash.
+#------------------------------------------------------------------------
+
+sub new {
+ my $class = shift;
+ my $params = ref $_[0] eq 'HASH' ? shift(@_) : { @_ };
+
+ my $self = {
+ global => { },
+ %$params,
+ %$ROOT_OPS,
+ '_PARENT' => undef,
+ };
+
+ bless $self, $class;
+}
+
+
+#========================================================================
+# ----- PUBLIC OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# clone(\%params)
+#
+# Creates a copy of the current stash object to effect localisation
+# of variables. The new stash is blessed into the same class as the
+# parent (which may be a derived class) and has a '_PARENT' member added
+# which contains a reference to the parent stash that created it
+# ($self). This member is used in a successive declone() method call to
+# return the reference to the parent.
+#
+# A parameter may be provided which should reference a hash of
+# variable/values which should be defined in the new stash. The
+# update() method is called to define these new variables in the cloned
+# stash.
+#
+# Returns a reference to a cloned Template::Stash.
+#------------------------------------------------------------------------
+
+sub clone {
+ my ($self, $params) = @_;
+ $params ||= { };
+
+ # look out for magical 'import' argument which imports another hash
+ my $import = $params->{ import };
+ if (defined $import && UNIVERSAL::isa($import, 'HASH')) {
+ delete $params->{ import };
+ }
+ else {
+ undef $import;
+ }
+
+ my $clone = bless {
+ %$self, # copy all parent members
+ %$params, # copy all new data
+ '_PARENT' => $self, # link to parent
+ }, ref $self;
+
+ # perform hash import if defined
+ &{ $HASH_OPS->{ import }}($clone, $import)
+ if defined $import;
+
+ return $clone;
+}
+
+
+#------------------------------------------------------------------------
+# declone($export)
+#
+# Returns a reference to the PARENT stash. When called in the following
+# manner:
+# $stash = $stash->declone();
+# the reference count on the current stash will drop to 0 and be "freed"
+# and the caller will be left with a reference to the parent. This
+# contains the state of the stash before it was cloned.
+#------------------------------------------------------------------------
+
+sub declone {
+ my $self = shift;
+ $self->{ _PARENT } || $self;
+}
+
+
+#------------------------------------------------------------------------
+# get($ident)
+#
+# Returns the value for an variable stored in the stash. The variable
+# may be specified as a simple string, e.g. 'foo', or as an array
+# reference representing compound variables. In the latter case, each
+# pair of successive elements in the list represent a node in the
+# compound variable. The first is the variable name, the second a
+# list reference of arguments or 0 if undefined. So, the compound
+# variable [% foo.bar('foo').baz %] would be represented as the list
+# [ 'foo', 0, 'bar', ['foo'], 'baz', 0 ]. Returns the value of the
+# identifier or an empty string if undefined. Errors are thrown via
+# die().
+#------------------------------------------------------------------------
+
+sub get {
+ my ($self, $ident, $args) = @_;
+ my ($root, $result);
+ $root = $self;
+
+ if (ref $ident eq 'ARRAY'
+ || ($ident =~ /\./)
+ && ($ident = [ map { s/\(.*$//; ($_, 0) } split(/\./, $ident) ])) {
+ my $size = $#$ident;
+
+ # if $ident is a list reference, then we evaluate each item in the
+ # identifier against the previous result, using the root stash
+ # ($self) as the first implicit 'result'...
+
+ foreach (my $i = 0; $i <= $size; $i += 2) {
+ if ( $i + 2 <= $size && ($ident->[$i+2] eq "scalar"
+ || $ident->[$i+2] eq "ref") ) {
+ $result = $self->_dotop($root, @$ident[$i, $i+1], 0,
+ $ident->[$i+2]);
+ $i += 2;
+ } else {
+ $result = $self->_dotop($root, @$ident[$i, $i+1]);
+ }
+ last unless defined $result;
+ $root = $result;
+ }
+ }
+ else {
+ $result = $self->_dotop($root, $ident, $args);
+ }
+
+ return defined $result ? $result : '';
+}
+
+
+#------------------------------------------------------------------------
+# set($ident, $value, $default)
+#
+# Updates the value for a variable in the stash. The first parameter
+# should be the variable name or array, as per get(). The second
+# parameter should be the intended value for the variable. The third,
+# optional parameter is a flag which may be set to indicate 'default'
+# mode. When set true, the variable will only be updated if it is
+# currently undefined or has a false value. The magical 'IMPORT'
+# variable identifier may be used to indicate that $value is a hash
+# reference whose values should be imported. Returns the value set,
+# or an empty string if not set (e.g. default mode). In the case of
+# IMPORT, returns the number of items imported from the hash.
+#------------------------------------------------------------------------
+
+sub set {
+ my ($self, $ident, $value, $default) = @_;
+ my ($root, $result, $error);
+
+ $root = $self;
+
+ ELEMENT: {
+ if (ref $ident eq 'ARRAY'
+ || ($ident =~ /\./)
+ && ($ident = [ map { s/\(.*$//; ($_, 0) }
+ split(/\./, $ident) ])) {
+
+ # a compound identifier may contain multiple elements (e.g.
+ # foo.bar.baz) and we must first resolve all but the last,
+ # using _dotop() with the $lvalue flag set which will create
+ # intermediate hashes if necessary...
+ my $size = $#$ident;
+ foreach (my $i = 0; $i < $size - 2; $i += 2) {
+ $result = $self->_dotop($root, @$ident[$i, $i+1], 1);
+ last ELEMENT unless defined $result;
+ $root = $result;
+ }
+
+ # then we call _assign() to assign the value to the last element
+ $result = $self->_assign($root, @$ident[$size-1, $size],
+ $value, $default);
+ }
+ else {
+ $result = $self->_assign($root, $ident, 0, $value, $default);
+ }
+ }
+
+ return defined $result ? $result : '';
+}
+
+
+#------------------------------------------------------------------------
+# getref($ident)
+#
+# Returns a "reference" to a particular item. This is represented as a
+# closure which will return the actual stash item when called.
+# WARNING: still experimental!
+#------------------------------------------------------------------------
+
+sub getref {
+ my ($self, $ident, $args) = @_;
+ my ($root, $item, $result);
+ $root = $self;
+
+ if (ref $ident eq 'ARRAY') {
+ my $size = $#$ident;
+
+ foreach (my $i = 0; $i <= $size; $i += 2) {
+ ($item, $args) = @$ident[$i, $i + 1];
+ last if $i >= $size - 2; # don't evaluate last node
+ last unless defined
+ ($root = $self->_dotop($root, $item, $args));
+ }
+ }
+ else {
+ $item = $ident;
+ }
+
+ if (defined $root) {
+ return sub { my @args = (@{$args||[]}, @_);
+ $self->_dotop($root, $item, \@args);
+ }
+ }
+ else {
+ return sub { '' };
+ }
+}
+
+
+
+
+#------------------------------------------------------------------------
+# update(\%params)
+#
+# Update multiple variables en masse. No magic is performed. Simple
+# variable names only.
+#------------------------------------------------------------------------
+
+sub update {
+ my ($self, $params) = @_;
+
+ # look out for magical 'import' argument to import another hash
+ my $import = $params->{ import };
+ if (defined $import && UNIVERSAL::isa($import, 'HASH')) {
+ @$self{ keys %$import } = values %$import;
+ delete $params->{ import };
+ }
+
+ @$self{ keys %$params } = values %$params;
+}
+
+
+#========================================================================
+# ----- PRIVATE OBJECT METHODS -----
+#========================================================================
+
+#------------------------------------------------------------------------
+# _dotop($root, $item, \@args, $lvalue, $nextItem)
+#
+# This is the core 'dot' operation method which evaluates elements of
+# variables against their root. All variables have an implicit root
+# which is the stash object itself (a hash). Thus, a non-compound
+# variable 'foo' is actually '(stash.)foo', the compound 'foo.bar' is
+# '(stash.)foo.bar'. The first parameter is a reference to the current
+# root, initially the stash itself. The second parameter contains the
+# name of the variable element, e.g. 'foo'. The third optional
+# parameter is a reference to a list of any parenthesised arguments
+# specified for the variable, which are passed to sub-routines, object
+# methods, etc. The final parameter is an optional flag to indicate
+# if this variable is being evaluated on the left side of an assignment
+# (e.g. foo.bar.baz = 10). When set true, intermediated hashes will
+# be created (e.g. bar) if necessary.
+#
+# Returns the result of evaluating the item against the root, having
+# performed any variable "magic". The value returned can then be used
+# as the root of the next _dotop() in a compound sequence. Returns
+# undef if the variable is undefined.
+#------------------------------------------------------------------------
+
+sub _dotop {
+ my ($self, $root, $item, $args, $lvalue, $nextItem) = @_;
+ my $rootref = ref $root;
+ my ($value, @result, $ret, $retVal);
+ $nextItem ||= "";
+ my $scalarContext = 1 if ( $nextItem eq "scalar" );
+ my $returnRef = 1 if ( $nextItem eq "ref" );
+
+ $args ||= [ ];
+ $lvalue ||= 0;
+
+# print STDERR "_dotop(root=$root, item=$item, args=[@$args])\n"
+# if $DEBUG;
+
+ # return undef without an error if either side of the dot is unviable
+ # or if an attempt is made to access a private member, starting _ or .
+ return undef
+ unless defined($root) and defined($item) and $item !~ /^[\._]/;
+
+ if (ref(\$root) eq "SCALAR" && !$lvalue &&
+ (($value = $LIST_OPS->{ $item }) || $item =~ /^-?\d+$/) ) {
+ #
+ # Promote scalar to one element list, to be processed below.
+ #
+ $rootref = 'ARRAY';
+ $root = [$root];
+ }
+ if ($rootref eq __PACKAGE__ || $rootref eq 'HASH') {
+
+ # if $root is a regular HASH or a Template::Stash kinda HASH (the
+ # *real* root of everything). We first lookup the named key
+ # in the hash, or create an empty hash in its place if undefined
+ # and the $lvalue flag is set. Otherwise, we check the HASH_OPS
+ # pseudo-methods table, calling the code if found, or return undef.
+
+ if (defined($value = $root->{ $item })) {
+ ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef,
+ $scalarContext);
+ return $retVal if ( $ret ); ## RETURN
+ }
+ elsif ($lvalue) {
+ # we create an intermediate hash if this is an lvalue
+ return $root->{ $item } = { }; ## RETURN
+ }
+ elsif ($value = $HASH_OPS->{ $item }) {
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif (ref $item eq 'ARRAY') {
+ # hash slice
+ return [@$root{@$item}]; ## RETURN
+ }
+ elsif ($value = $SCALAR_OPS->{ $item }) {
+ #
+ # Apply scalar ops to every hash element, in place.
+ #
+ foreach my $key ( keys %$root ) {
+ $root->{$key} = &$value($root->{$key}, @$args);
+ }
+ }
+ }
+ elsif ($rootref eq 'ARRAY') {
+
+ # if root is an ARRAY then we check for a LIST_OPS pseudo-method
+ # (except for l-values for which it doesn't make any sense)
+ # or return the numerical index into the array, or undef
+
+ if (($value = $LIST_OPS->{ $item }) && ! $lvalue) {
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) {
+ #
+ # Apply scalar ops to every array element, in place.
+ #
+ for ( my $i = 0 ; $i < @$root ; $i++ ) {
+ $root->[$i] = &$value($root->[$i], @$args); ## @result
+ }
+ }
+ elsif ($item =~ /^-?\d+$/) {
+ $value = $root->[$item];
+ ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef,
+ $scalarContext);
+ return $retVal if ( $ret ); ## RETURN
+ }
+ elsif (ref $item eq 'ARRAY' ) {
+ # array slice
+ return [@$root[@$item]]; ## RETURN
+ }
+ }
+
+ # NOTE: we do the can-can because UNIVSERAL::isa($something, 'UNIVERSAL')
+ # doesn't appear to work with CGI, returning true for the first call
+ # and false for all subsequent calls.
+
+ elsif (ref($root) && UNIVERSAL::can($root, 'can')) {
+
+ # if $root is a blessed reference (i.e. inherits from the
+ # UNIVERSAL object base class) then we call the item as a method.
+ # If that fails then we try to fallback on HASH behaviour if
+ # possible.
+ return ref $root->can($item) if ( $returnRef ); ## RETURN
+ eval {
+ @result = $scalarContext ? scalar $root->$item(@$args)
+ : $root->$item(@$args); ## @result
+ };
+
+ if ($@) {
+ # failed to call object method, so try some fallbacks
+ if (UNIVERSAL::isa($root, 'HASH')
+ && defined($value = $root->{ $item })) {
+ ($ret, $retVal, @result) = _dotop_return($value, $args,
+ $returnRef, $scalarContext);
+ return $retVal if ( $ret ); ## RETURN
+ }
+ elsif (UNIVERSAL::isa($root, 'ARRAY')
+ && ($value = $LIST_OPS->{ $item })) {
+ @result = &$value($root, @$args);
+ }
+ else {
+ @result = (undef, $@);
+ }
+ }
+ }
+ elsif (($value = $SCALAR_OPS->{ $item }) && ! $lvalue) {
+
+ # at this point, it doesn't look like we've got a reference to
+ # anything we know about, so we try the SCALAR_OPS pseudo-methods
+ # table (but not for l-values)
+
+ @result = &$value($root, @$args); ## @result
+ }
+ elsif ($self->{ _DEBUG }) {
+ die "don't know how to access [ $root ].$item\n"; ## DIE
+ }
+ else {
+ @result = ();
+ }
+
+ # fold multiple return items into a list unless first item is undef
+ if (defined $result[0]) {
+ return ref(@result > 1 ? [ @result ] : $result[0])
+ if ( $returnRef ); ## RETURN
+ if ( $scalarContext ) {
+ return scalar @result if ( @result > 1 ); ## RETURN
+ return scalar(@{$result[0]}) if ( ref $result[0] eq "ARRAY" );
+ return scalar(%{$result[0]}) if ( ref $result[0] eq "HASH" );
+ return $result[0]; ## RETURN
+ } else {
+ return @result > 1 ? [ @result ] : $result[0]; ## RETURN
+ }
+ }
+ elsif (defined $result[1]) {
+ die $result[1]; ## DIE
+ }
+ elsif ($self->{ _DEBUG }) {
+ die "$item is undefined\n"; ## DIE
+ }
+
+ return undef;
+}
+
+#------------------------------------------------------------------------
+# ($ret, $retVal, @result) = _dotop_return($value, $args, $returnRef,
+# $scalarContext);
+#
+# Handle the various return processing for _dotop
+#------------------------------------------------------------------------
+sub _dotop_return
+{
+ my($value, $args, $returnRef, $scalarContext) = @_;
+ my(@result);
+
+ return (1, ref $value) if ( $returnRef ); ## RETURN
+ if ( $scalarContext ) {
+ return (1, scalar(@$value)) if ref $value eq 'ARRAY'; ## RETURN
+ return (1, scalar(%$value)) if ref $value eq 'HASH'; ## RETURN
+ return (1, scalar($value)) unless ref $value eq 'CODE'; ## RETURN;
+ @result = scalar &$value(@$args) ## @result;
+ } else {
+ return (1, $value) unless ref $value eq 'CODE'; ## RETURN
+ @result = &$value(@$args); ## @result
+ }
+ return (0, undef, @result);
+}
+
+
+#------------------------------------------------------------------------
+# _assign($root, $item, \@args, $value, $default)
+#
+# Similar to _dotop() above, but assigns a value to the given variable
+# instead of simply returning it. The first three parameters are the
+# root item, the item and arguments, as per _dotop(), followed by the
+# value to which the variable should be set and an optional $default
+# flag. If set true, the variable will only be set if currently false
+# (undefined/zero)
+#------------------------------------------------------------------------
+
+sub _assign {
+ my ($self, $root, $item, $args, $value, $default) = @_;
+ my $rootref = ref $root;
+ my $result;
+ $args ||= [ ];
+ $default ||= 0;
+
+# print(STDERR "_assign(root=$root, item=$item, args=[@$args], \n",
+# "value=$value, default=$default)\n")
+# if $DEBUG;
+
+ # return undef without an error if either side of the dot is unviable
+ # or if an attempt is made to update a private member, starting _ or .
+ return undef ## RETURN
+ unless $root and defined $item and $item !~ /^[\._]/;
+
+ if ($rootref eq 'HASH' || $rootref eq __PACKAGE__) {
+# if ($item eq 'IMPORT' && UNIVERSAL::isa($value, 'HASH')) {
+# # import hash entries into root hash
+# @$root{ keys %$value } = values %$value;
+# return ''; ## RETURN
+# }
+ # if the root is a hash we set the named key
+ return ($root->{ $item } = $value) ## RETURN
+ unless $default && $root->{ $item };
+ }
+ elsif ($rootref eq 'ARRAY' && $item =~ /^-?\d+$/) {
+ # or set a list item by index number
+ return ($root->[$item] = $value) ## RETURN
+ unless $default && $root->{ $item };
+ }
+ elsif (UNIVERSAL::isa($root, 'UNIVERSAL')) {
+ # try to call the item as a method of an object
+ return $root->$item(@$args, $value); ## RETURN
+ }
+ else {
+ die "don't know how to assign to [$root].[$item]\n"; ## DIE
+ }
+
+ return undef;
+}
+
+
+#------------------------------------------------------------------------
+# _dump()
+#
+# Debug method which returns a string representing the internal state
+# of the object. The method calls itself recursively to dump sub-hashes.
+#------------------------------------------------------------------------
+
+sub _dump {
+ my $self = shift;
+ my $indent = shift || 1;
+ my $buffer = ' ';
+ my $pad = $buffer x $indent;
+ my $text = '';
+ local $" = ', ';
+
+ my ($key, $value);
+
+
+ return $text . "...excessive recursion, terminating\n"
+ if $indent > 32;
+
+ foreach $key (keys %$self) {
+
+ $value = $self->{ $key };
+ $value = '<undef>' unless defined $value;
+
+ if (ref($value) eq 'ARRAY') {
+ $value = "$value [@$value]";
+ }
+ $text .= sprintf("$pad%-8s => $value\n", $key);
+ next if $key =~ /^\./;
+ if (UNIVERSAL::isa($value, 'HASH')) {
+ $text .= _dump($value, $indent + 1);
+ }
+ }
+ $text;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Stash::Context - Experimetal stash allowing list/scalar context definition
+
+=head1 SYNOPSIS
+
+ use Template;
+ use Template::Stash::Context;
+
+ my $stash = Template::Stash::Context->new(\%vars);
+ my $tt2 = Template->new({ STASH => $stash });
+
+=head1 DESCRIPTION
+
+This is an alternate stash object which includes a patch from
+Craig Barratt to implement various new virtual methods to allow
+dotted template variable to denote if object methods and subroutines
+should be called in scalar or list context. It adds a little overhead
+to each stash call and I'm a little wary of applying that to the core
+default stash without investigating the effects first. So for now,
+it's implemented as a separate stash module which will allow us to
+test it out, benchmark it and switch it in or out as we require.
+
+This is what Craig has to say about it:
+
+Here's a better set of features for the core. Attached is a new version
+of Stash.pm (based on TT2.02) that:
+
+* supports the special op "scalar" that forces scalar context on
+function calls, eg:
+
+ cgi.param("foo").scalar
+
+calls cgi.param("foo") in scalar context (unlike my wimpy
+scalar op from last night). Array context is the default.
+
+With non-function operands, scalar behaves like the perl
+version (eg: no-op for scalar, size for arrays, etc).
+
+* supports the special op "ref" that behaves like the perl ref.
+If applied to a function the function is not called. Eg:
+
+ cgi.param("foo").ref
+
+does *not* call cgi.param and evaluates to "CODE". Similarly,
+HASH.ref, ARRAY.ref return what you expect.
+
+* adds a new scalar and list op called "array" that is a no-op for
+arrays and promotes scalars to one-element arrays.
+
+* allows scalar ops to be applied to arrays and hashes in place,
+eg: ARRAY.repeat(3) repeats each element in place.
+
+* allows list ops to be applied to scalars by promoting the scalars
+to one-element arrays (like an implicit "array"). So you can
+do things like SCALAR.size, SCALAR.join and get a useful result.
+
+This also means you can now use x.0 to safely get the first element
+whether x is an array or scalar.
+
+The new Stash.pm passes the TT2.02 test suite. But I haven't tested the
+new features very much. One nagging implementation problem is that the
+"scalar" and "ref" ops have higher precedence than user variable names.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+1.53, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Stash|Template::Stash>
diff --git a/lib/Template/Stash/XS.pm b/lib/Template/Stash/XS.pm
new file mode 100644
index 0000000..ca37c08
--- /dev/null
+++ b/lib/Template/Stash/XS.pm
@@ -0,0 +1,176 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Stash::XS
+#
+# DESCRIPTION
+#
+# Perl bootstrap for XS module. Inherits methods from
+# Template::Stash when not implemented in the XS module.
+#
+#========================================================================
+
+package Template::Stash::XS;
+
+use Template;
+use Template::Stash;
+
+BEGIN {
+ require DynaLoader;
+ @Template::Stash::XS::ISA = qw( DynaLoader Template::Stash );
+
+ eval {
+ bootstrap Template::Stash::XS $Template::VERSION;
+ };
+ if ($@) {
+ die "Couldn't load Template::Stash::XS $Template::VERSION:\n\n$@\n";
+ }
+}
+
+
+sub DESTROY {
+ # no op
+ 1;
+}
+
+
+# catch missing method calls here so perl doesn't barf
+# trying to load *.al files
+sub AUTOLOAD {
+ my ($self, @args) = @_;
+ my @c = caller(0);
+ my $auto = $AUTOLOAD;
+
+ $auto =~ s/.*:://;
+ $self =~ s/=.*//;
+
+ die "Can't locate object method \"$auto\"" .
+ " via package \"$self\" at $c[1] line $c[2]\n";
+}
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Stash::XS - Experimetal high-speed stash written in XS
+
+=head1 SYNOPSIS
+
+ use Template;
+ use Template::Stash::XS;
+
+ my $stash = Template::Stash::XS->new(\%vars);
+ my $tt2 = Template->new({ STASH => $stash });
+
+=head1 DESCRIPTION
+
+This module loads the XS version of Template::Stash::XS. It should
+behave very much like the old one, but run about twice as fast.
+See the synopsis above for usage information.
+
+Only a few methods (such as get and set) have been implemented in XS.
+The others are inherited from Template::Stash.
+
+=head1 NOTE
+
+To always use the XS version of Stash, modify the Template/Config.pm
+module near line 45:
+
+ $STASH = 'Template::Stash::XS';
+
+If you make this change, then there is no need to explicitly create
+an instance of Template::Stash::XS as seen in the SYNOPSIS above. Just
+use Template as normal.
+
+Alternatively, in your code add this line before creating a Template
+object:
+
+ $Template::Config::STASH = 'Template::Stash::XS';
+
+To use the original, pure-perl version restore this line in
+Template/Config.pm:
+
+ $STASH = 'Template::Stash';
+
+Or in your code:
+
+ $Template::Config::STASH = 'Template::Stash';
+
+You can elect to have this performed once for you at installation
+time by answering 'y' or 'n' to the question that asks if you want
+to make the XS Stash the default.
+
+=head1 BUGS
+
+Please report bugs to the Template Toolkit mailing list
+templates@template-toolkit.org
+
+As of version 2.05 of the Template Toolkit, use of the XS Stash is
+known to have 2 potentially troublesome side effects. The first
+problem is that accesses to tied hashes (e.g. Apache::Session) may not
+work as expected. This should be fixed in an imminent release. If
+you are using tied hashes then it is suggested that you use the
+regular Stash by default, or write a thin wrapper around your tied
+hashes to enable the XS Stash to access items via regular method
+calls.
+
+The second potential problem is that enabling the XS Stash causes all
+the Template Toolkit modules to be installed in an architecture
+dependant library, e.g. in
+
+ /usr/lib/perl5/site_perl/5.6.0/i386-linux/Template
+
+instead of
+
+ /usr/lib/perl5/site_perl/5.6.0/Template
+
+At the time of writing, we're not sure why this is happening but it's
+likely that this is either a bug or intentional feature in the Perl
+ExtUtils::MakeMaker module. As far as I know, Perl always checks the
+architecture dependant directories before the architecture independant
+ones. Therefore, a newer version of the Template Toolkit installed
+with the XS Stash enabled should be used by Perl in preference to any
+existing version using the regular stash. However, if you install a
+future version of the Template Toolkit with the XS Stash disabled, you
+may find that Perl continues to use the older version with XS Stash
+enabled in preference.
+
+=head1 AUTHORS
+
+Andy Wardley E<lt>abw@tt2.orgE<gt>
+
+Doug Steinwand E<lt>dsteinwand@citysearch.comE<gt>
+
+=head1 VERSION
+
+Template Toolkit version 2.10, released on 24 July 2003.
+
+
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+
+
+=head1 SEE ALSO
+
+L<Template::Stash|Template::Stash>
+
diff --git a/lib/Template/Test.pm b/lib/Template/Test.pm
new file mode 100644
index 0000000..ba5915f
--- /dev/null
+++ b/lib/Template/Test.pm
@@ -0,0 +1,701 @@
+#============================================================= -*-Perl-*-
+#
+# Template::Test
+#
+# DESCRIPTION
+# Module defining a test harness which processes template input and
+# then compares the output against pre-define expected output.
+# Generates test output compatible with Test::Harness. This was
+# originally the t/texpect.pl script.
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 1996-2000 Andy Wardley. All Rights Reserved.
+# Copyright (C) 1998-2000 Canon Research Centre Europe Ltd.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+#----------------------------------------------------------------------------
+#
+# $Id: Test.pm,v 2.64 2003/04/29 12:29:49 abw Exp $
+#
+#============================================================================
+
+package Template::Test;
+
+require 5.004;
+
+use strict;
+use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS
+ $VERSION $DEBUG $EXTRA $PRESERVE $REASON $NO_FLUSH
+ $loaded %callsign);
+use Template qw( :template );
+use Exporter;
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.64 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0;
+@ISA = qw( Exporter );
+@EXPORT = qw( ntests ok is match flush skip_all test_expect callsign banner );
+@EXPORT_OK = ( 'assert' );
+%EXPORT_TAGS = ( all => [ @EXPORT_OK, @EXPORT ] );
+$| = 1;
+
+$REASON = 'not applicable on this platform';
+$NO_FLUSH = 0;
+$EXTRA = 0; # any extra tests to come after test_expect()
+$PRESERVE = 0 # don't mangle newlines in output/expect
+ unless defined $PRESERVE;
+
+# always set binmode on Win32 machines so that any output generated
+# is true to what we expect
+$Template::BINMODE = ($^O eq 'MSWin32') ? 1 : 0;
+
+my @results = ();
+my ($ntests, $ok_count);
+*is = \&match;
+
+END {
+ # ensure flush() is called to print any cached results
+ flush();
+}
+
+
+#------------------------------------------------------------------------
+# ntests($n)
+#
+# Declare how many (more) tests are expected to come. If ok() is called
+# before ntests() then the results are cached instead of being printed
+# to STDOUT. When ntests() is called, the total number of tests
+# (including any cached) is known and the "1..$ntests" line can be
+# printed along with the cached results. After that, calls to ok()
+# generated printed output immediately.
+#------------------------------------------------------------------------
+
+sub ntests {
+ $ntests = shift;
+ # add any pre-declared extra tests, or pre-stored test @results, to
+ # the grand total of tests
+ $ntests += $EXTRA + scalar @results;
+ $ok_count = 1;
+ print $ntests ? "1..$ntests\n" : "1..$ntests # skipped: $REASON\n";
+ # flush cached results
+ foreach my $pre_test (@results) {
+ ok(@$pre_test);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# ok($truth, $msg)
+#
+# Tests the value passed for truth and generates an "ok $n" or "not ok $n"
+# line accordingly. If ntests() hasn't been called then we cached
+# results for later, instead.
+#------------------------------------------------------------------------
+
+sub ok {
+ my ($ok, $msg) = @_;
+
+ # cache results if ntests() not yet called
+ unless ($ok_count) {
+ push(@results, [ $ok, $msg ]);
+ return $ok;
+ }
+
+ $msg = defined $msg ? " - $msg" : '';
+ if ($ok) {
+ print "ok ", $ok_count++, "$msg\n";
+ }
+ else {
+ print STDERR "FAILED $ok_count: $msg\n" if defined $msg;
+ print "not ok ", $ok_count++, "$msg\n";
+ }
+}
+
+
+
+#------------------------------------------------------------------------
+# assert($truth, $error)
+#
+# Test value for truth, die if false.
+#------------------------------------------------------------------------
+
+sub assert {
+ my ($ok, $err) = @_;
+ return ok(1) if $ok;
+
+ # failed
+ my ($pkg, $file, $line) = caller();
+ $err ||= "assert failed";
+ $err .= " at $file line $line\n";
+ ok(0);
+ die $err;
+}
+
+#------------------------------------------------------------------------
+# match( $result, $expect )
+#------------------------------------------------------------------------
+
+sub match {
+ my ($result, $expect, $msg) = @_;
+ my $count = $ok_count ? $ok_count : scalar @results + 1;
+
+ # force stringification of $result to avoid 'no eq method' overload errors
+ $result = "$result" if ref $result;
+
+ if ($result eq $expect) {
+ return ok(1, $msg);
+ }
+ else {
+ print STDERR "FAILED $count:\n expect: [$expect]\n result: [$result]\n";
+ return ok(0, $msg);
+ }
+}
+
+
+#------------------------------------------------------------------------
+# flush()
+#
+# Flush any tests results.
+#------------------------------------------------------------------------
+
+sub flush {
+ ntests(0)
+ unless $ok_count || $NO_FLUSH;
+}
+
+
+#------------------------------------------------------------------------
+# skip_all($reason)
+#
+# Skip all tests, setting $REASON to contain any message passed. Calls
+# exit(0) which triggers flush() which generates a "1..0 # $REASON"
+# string to keep to test harness happy.
+#------------------------------------------------------------------------
+
+sub skip_all {
+ $REASON = join('', @_);
+ exit(0);
+}
+
+
+#------------------------------------------------------------------------
+# test_expect($input, $template, \%replace)
+#
+# This is the main testing sub-routine. The $input parameter should be a
+# text string or a filehandle reference (e.g. GLOB or IO::Handle) from
+# which the input text can be read. The input should contain a number
+# of tests which are split up and processed individually, comparing the
+# generated output against the expected output. Tests should be defined
+# as follows:
+#
+# -- test --
+# test input
+# -- expect --
+# expected output
+#
+# -- test --
+# etc...
+#
+# The number of tests is determined and ntests() is called to generate
+# the "0..$n" line compatible with Test::Harness. Each test input is
+# then processed by the Template object passed as the second parameter,
+# $template. This may also be a hash reference containing configuration
+# which are used to instantiate a Template object, or may be left
+# undefined in which case a default Template object will be instantiated.
+# The third parameter, also optional, may be a reference to a hash array
+# defining template variables. This is passed to the template process()
+# method.
+#------------------------------------------------------------------------
+
+sub test_expect {
+ my ($src, $tproc, $params) = @_;
+ my ($input, @tests);
+ my ($output, $expect, $match);
+ my $count = 0;
+ my $ttprocs;
+
+ # read input text
+ eval {
+ local $/ = undef;
+ $input = ref $src ? <$src> : $src;
+ };
+ if ($@) {
+ ntests(1); ok(0);
+ warn "Cannot read input text from $src\n";
+ return undef;
+ }
+
+ # remove any comment lines
+ $input =~ s/^#.*?\n//gm;
+
+ # remove anything before '-- start --' and/or after '-- stop --'
+ $input = $' if $input =~ /\s*--\s*start\s*--\s*/;
+ $input = $` if $input =~ /\s*--\s*stop\s*--\s*/;
+
+ @tests = split(/^\s*--\s*test\s*--\s*\n/im, $input);
+
+ # if the first line of the file was '--test--' (optional) then the
+ # first test will be empty and can be discarded
+ shift(@tests) if $tests[0] =~ /^\s*$/;
+
+ ntests(3 + scalar(@tests) * 2);
+
+ # first test is that Template loaded OK, which it did
+ ok(1, 'running test_expect()');
+
+ # optional second param may contain a Template reference or a HASH ref
+ # of constructor options, or may be undefined
+ if (ref($tproc) eq 'HASH') {
+ # create Template object using hash of config items
+ $tproc = Template->new($tproc)
+ || die Template->error(), "\n";
+ }
+ elsif (ref($tproc) eq 'ARRAY') {
+ # list of [ name => $tproc, name => $tproc ], use first $tproc
+ $ttprocs = { @$tproc };
+ $tproc = $tproc->[1];
+ }
+ elsif (! ref $tproc) {
+ $tproc = Template->new()
+ || die Template->error(), "\n";
+ }
+ # otherwise, we assume it's a Template reference
+
+ # test: template processor created OK
+ ok($tproc, 'template processor is engaged');
+
+ # third test is that the input read ok, which it did
+ ok(1, 'input read and split into ' . scalar @tests . ' tests');
+
+ # the remaining tests are defined in @tests...
+ foreach $input (@tests) {
+ $count++;
+ my $name = '';
+
+ if ($input =~ s/^\s*-- name:? (.*?) --\s*\n//im) {
+ $name = $1;
+ }
+ else {
+ $name = "template text $count";
+ }
+
+ # split input by a line like "-- expect --"
+ ($input, $expect) =
+ split(/^\s*--\s*expect\s*--\s*\n/im, $input);
+ $expect = ''
+ unless defined $expect;
+
+ $output = '';
+
+ # input text may be prefixed with "-- use name --" to indicate a
+ # Template object in the $ttproc hash which we should use
+ if ($input =~ s/^\s*--\s*use\s+(\S+)\s*--\s*\n//im) {
+ my $ttname = $1;
+ my $ttlookup;
+ if ($ttlookup = $ttprocs->{ $ttname }) {
+ $tproc = $ttlookup;
+ }
+ else {
+ warn "no such template object to use: $ttname\n";
+ }
+ }
+
+ # process input text
+ $tproc->process(\$input, $params, \$output) || do {
+ warn "Template process failed: ", $tproc->error(), "\n";
+ # report failure and automatically fail the expect match
+ ok(0, "$name process FAILED: " . subtext($input));
+ ok(0, '(obviously did not match expected)');
+ next;
+ };
+
+ # processed OK
+ ok(1, "$name processed OK: " . subtext($input));
+
+ # another hack: if the '-- expect --' section starts with
+ # '-- process --' then we process the expected output
+ # before comparing it with the generated output. This is
+ # slightly twisted but it makes it possible to run tests
+ # where the expected output isn't static. See t/date.t for
+ # an example.
+
+ if ($expect =~ s/^\s*--+\s*process\s*--+\s*\n//im) {
+ my $out;
+ $tproc->process(\$expect, $params, \$out) || do {
+ warn("Template process failed (expect): ",
+ $tproc->error(), "\n");
+ # report failure and automatically fail the expect match
+ ok(0, "failed to process expected output ["
+ . subtext($expect) . ']');
+ next;
+ };
+ $expect = $out;
+ };
+
+ # strip any trailing blank lines from expected and real output
+ foreach ($expect, $output) {
+ s/\n*\Z//mg;
+ }
+
+ $match = ($expect eq $output) ? 1 : 0;
+ if (! $match || $DEBUG) {
+ print "MATCH FAILED\n"
+ unless $match;
+
+ my ($copyi, $copye, $copyo) = ($input, $expect, $output);
+ unless ($PRESERVE) {
+ foreach ($copyi, $copye, $copyo) {
+ s/\n/\\n/g;
+ }
+ }
+ printf(" input: [%s]\nexpect: [%s]\noutput: [%s]\n",
+ $copyi, $copye, $copyo);
+ }
+
+ ok($match, $match ? "$name matched expected" : "$name did not match expected");
+ };
+}
+
+#------------------------------------------------------------------------
+# callsign()
+#
+# Returns a hash array mapping lower a..z to their phonetic alphabet
+# equivalent.
+#------------------------------------------------------------------------
+
+sub callsign {
+ my %callsign;
+ @callsign{ 'a'..'z' } = qw(
+ alpha bravo charlie delta echo foxtrot golf hotel india
+ juliet kilo lima mike november oscar papa quebec romeo
+ sierra tango umbrella victor whisky x-ray yankee zulu );
+ return \%callsign;
+}
+
+
+#------------------------------------------------------------------------
+# banner($text)
+#
+# Prints a banner with the specified text if $DEBUG is set.
+#------------------------------------------------------------------------
+
+sub banner {
+ return unless $DEBUG;
+ my $text = join('', @_);
+ my $count = $ok_count ? $ok_count - 1 : scalar @results;
+ print "-" x 72, "\n$text ($count tests completed)\n", "-" x 72, "\n";
+}
+
+
+sub subtext {
+ my $text = shift;
+ $text =~ s/\s*$//sg;
+ $text = substr($text, 0, 32) . '...' if length $text > 32;
+ $text =~ s/\n/\\n/g;
+ return $text;
+}
+
+
+1;
+
+__END__
+
+
+#------------------------------------------------------------------------
+# IMPORTANT NOTE
+# This documentation is generated automatically from source
+# templates. Any changes you make here may be lost.
+#
+# The 'docsrc' documentation source bundle is available for download
+# from http://www.template-toolkit.org/docs.html and contains all
+# the source templates, XML files, scripts, etc., from which the
+# documentation for the Template Toolkit is built.
+#------------------------------------------------------------------------
+
+=head1 NAME
+
+Template::Test - Module for automating TT2 test scripts
+
+=head1 SYNOPSIS
+
+ use Template::Test;
+
+ $Template::Test::DEBUG = 0; # set this true to see each test running
+ $Template::Test::EXTRA = 2; # 2 extra tests follow test_expect()...
+
+ # ok() can be called any number of times before test_expect
+ ok( $true_or_false )
+
+ # test_expect() splits $input into individual tests, processes each
+ # and compares generated output against expected output
+ test_expect($input, $template, \%replace );
+
+ # $input is text or filehandle (e.g. DATA section after __END__)
+ test_expect( $text );
+ test_expect( \*DATA );
+
+ # $template is a Template object or configuration hash
+ my $template_cfg = { ... };
+ test_expect( $input, $template_cfg );
+ my $template_obj = Template->new($template_cfg);
+ test_expect( $input, $template_obj );
+
+ # $replace is a hash reference of template variables
+ my $replace = {
+ a => 'alpha',
+ b => 'bravo'
+ };
+ test_expect( $input, $template, $replace );
+
+ # ok() called after test_expect should be declared in $EXTRA (2)
+ ok( $true_or_false )
+ ok( $true_or_false )
+
+=head1 DESCRIPTION
+
+The Template::Test module defines the test_expect() and other related
+subroutines which can be used to automate test scripts for the
+Template Toolkit. See the numerous tests in the 't' sub-directory of
+the distribution for examples of use.
+
+The test_expect() subroutine splits an input document into a number
+of separate tests, processes each one using the Template Toolkit and
+then compares the generated output against an expected output, also
+specified in the input document. It generates the familiar "ok/not
+ok" output compatible with Test::Harness.
+
+The test input should be specified as a text string or a reference to
+a filehandle (e.g. GLOB or IO::Handle) from which it can be read. In
+particular, this allows the test input to be placed after the __END__
+marker and read via the DATA filehandle.
+
+ use Template::Test;
+
+ test_expect(\*DATA);
+
+ __END__
+ # this is the first test (this is a comment)
+ -- test --
+ blah blah blah [% foo %]
+ -- expect --
+ blah blah blah value_of_foo
+
+ # here's the second test (no surprise, so is this)
+ -- test --
+ more blah blah [% bar %]
+ -- expect --
+ more blah blah value_of_bar
+
+Blank lines between test sections are generally ignored. Any line starting
+with '#' is treated as a comment and is ignored.
+
+The second and third parameters to test_expect() are optional. The second
+may be either a reference to a Template object which should be used to
+process the template fragments, or a reference to a hash array containing
+configuration values which should be used to instantiate a new Template
+object.
+
+ # pass reference to config hash
+ my $config = {
+ INCLUDE_PATH => '/here/there:/every/where',
+ POST_CHOMP => 1,
+ };
+ test_expect(\*DATA, $config);
+
+ # or create Template object explicitly
+ my $template = Template->new($config);
+ test_expect(\*DATA, $template);
+
+
+The third parameter may be used to reference a hash array of template
+variable which should be defined when processing the tests. This is
+passed to the Template process() method.
+
+ my $replace = {
+ a => 'alpha',
+ b => 'bravo',
+ };
+
+ test_expect(\*DATA, $config, $replace);
+
+The second parameter may be left undefined to specify a default Template
+configuration.
+
+ test_expect(\*DATA, undef, $replace);
+
+For testing the output of different Template configurations, a
+reference to a list of named Template objects also may be passed as
+the second parameter.
+
+ my $tt1 = Template->new({ ... });
+ my $tt2 = Template->new({ ... });
+ my @tts = [ one => $tt1, two => $tt1 ];
+
+The first object in the list is used by default. Other objects may be
+switched in with the '-- use $name --' marker. This should immediately
+follow a '-- test --' line. That object will then be used for the rest
+of the test, or until a different object is selected.
+
+ -- test --
+ -- use one --
+ [% blah %]
+ -- expect --
+ blah, blah
+
+ -- test --
+ still using one...
+ -- expect --
+ ...
+
+ -- test --
+ -- use two --
+ [% blah %]
+ -- expect --
+ blah, blah, more blah
+
+The test_expect() sub counts the number of tests, and then calls ntests()
+to generate the familiar "1..$ntests\n" test harness line. Each
+test defined generates two test numbers. The first indicates
+that the input was processed without error, and the second that the
+output matches that expected.
+
+Additional test may be run before test_expect() by calling ok().
+These test results are cached until ntests() is called and the final
+number of tests can be calculated. Then, the "1..$ntests" line is
+output, along with "ok $n" / "not ok $n" lines for each of the cached
+test result. Subsequent calls to ok() then generate an output line
+immediately.
+
+ my $something = SomeObject->new();
+ ok( $something );
+
+ my $other = AnotherThing->new();
+ ok( $other );
+
+ test_expect(\*DATA);
+
+If any tests are to follow after test_expect() is called then these
+should be pre-declared by setting the $EXTRA package variable. This
+value (default: 0) is added to the grand total calculated by ntests().
+The results of the additional tests are also registered by calling ok().
+
+ $Template::Test::EXTRA = 2;
+
+ # can call ok() any number of times before test_expect()
+ ok( $did_that_work );
+ ok( $make_sure );
+ ok( $dead_certain );
+
+ # <some> number of tests...
+ test_expect(\*DATA, $config, $replace);
+
+ # here's those $EXTRA tests
+ ok( defined $some_result && ref $some_result eq 'ARRAY' );
+ ok( $some_result->[0] eq 'some expected value' );
+
+If you don't want to call test_expect() at all then you can call
+ntests($n) to declare the number of tests and generate the test
+header line. After that, simply call ok() for each test passing
+a true or false values to indicate that the test passed or failed.
+
+ ntests(2);
+ ok(1);
+ ok(0);
+
+If you're really lazy, you can just call ok() and not bother declaring
+the number of tests at all. All tests results will be cached until the
+end of the script and then printed in one go before the program exits.
+
+ ok( $x );
+ ok( $y );
+
+You can identify only a specific part of the input file for testing
+using the '-- start --' and '-- stop --' markers. Anything before the
+first '-- start --' is ignored, along with anything after the next
+'-- stop --' marker.
+
+ -- test --
+ this is test 1 (not performed)
+ -- expect --
+ this is test 1 (not performed)
+
+ -- start --
+
+ -- test --
+ this is test 2
+ -- expect --
+ this is test 2
+
+ -- stop --
+
+ ...
+
+For historical reasons and general utility, the module also defines a
+'callsign' subroutine which returns a hash mapping a..z to their phonetic
+alphabet equivalent (e.g. radio callsigns). This is used by many
+of the test scripts as a "known source" of variable values.
+
+ test_expect(\*DATA, $config, callsign());
+
+A banner() subroutine is also provided which prints a simple banner
+including any text passed as parameters, if $DEBUG is set.
+
+ banner('Testing something-or-other');
+
+example output:
+
+ #------------------------------------------------------------
+ # Testing something-or-other (27 tests completed)
+ #------------------------------------------------------------
+
+The $DEBUG package variable can be set to enable debugging mode.
+
+The $PRESERVE package variable can be set to stop the test_expect()
+from converting newlines in the output and expected output into
+the literal strings '\n'.
+
+=head1 HISTORY
+
+This module started its butt-ugly life as the t/texpect.pl script. It
+was cleaned up to became the Template::Test module some time around
+version 0.29. It underwent further cosmetic surgery for version 2.00
+but still retains some rear-end resemblances.
+
+=head1 BUGS / KNOWN "FEATURES"
+
+Imports all methods by default. This is generally a Bad Thing, but
+this module is only used in test scripts (i.e. at build time) so a) we
+don't really care and b) it saves typing.
+
+The line splitter may be a bit dumb, especially if it sees lines like
+-- this -- that aren't supposed to be special markers. So don't do that.
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@andywardley.comE<gt>
+
+L<http://www.andywardley.com/|http://www.andywardley.com/>
+
+
+
+
+=head1 VERSION
+
+2.64, distributed as part of the
+Template Toolkit version 2.10, released on 24 July 2003.
+
+=head1 COPYRIGHT
+
+ Copyright (C) 1996-2003 Andy Wardley. All Rights Reserved.
+ Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template|Template>
diff --git a/lib/Template/View.pm b/lib/Template/View.pm
new file mode 100644
index 0000000..312ff45
--- /dev/null
+++ b/lib/Template/View.pm
@@ -0,0 +1,754 @@
+#============================================================= -*-Perl-*-
+#
+# Template::View
+#
+# DESCRIPTION
+# A custom view of a template processing context. Can be used to
+# implement custom "skins".
+#
+# AUTHOR
+# Andy Wardley <abw@kfs.org>
+#
+# COPYRIGHT
+# Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+#
+# This module is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+#
+# TODO
+# * allowing print to have a hash ref as final args will cause problems
+# if you do this: [% view.print(hash1, hash2, hash3) %]. Current
+# work-around is to do [% view.print(hash1); view.print(hash2);
+# view.print(hash3) %] or [% view.print(hash1, hash2, hash3, { }) %]
+#
+# REVISION
+# $Id: View.pm,v 2.8 2002/04/15 15:53:37 abw Exp $
+#
+#============================================================================
+
+package Template::View;
+
+require 5.004;
+
+use strict;
+use vars qw( $VERSION $DEBUG $AUTOLOAD @BASEARGS $MAP );
+use base qw( Template::Base );
+
+$VERSION = sprintf("%d.%02d", q$Revision: 2.8 $ =~ /(\d+)\.(\d+)/);
+$DEBUG = 0 unless defined $DEBUG;
+@BASEARGS = qw( context );
+$MAP = {
+ HASH => 'hash',
+ ARRAY => 'list',
+ TEXT => 'text',
+ default => '',
+};
+
+#$DEBUG = 1;
+
+#------------------------------------------------------------------------
+# _init(\%config)
+#
+# Initialisation method called by the Template::Base class new()
+# constructor. $self->{ context } has already been set, by virtue of
+# being named in @BASEARGS. Remaining config arguments are presented
+# as a hash reference.
+#------------------------------------------------------------------------
+
+sub _init {
+ my ($self, $config) = @_;
+
+ # move 'context' somewhere more private
+ $self->{ _CONTEXT } = $self->{ context };
+ delete $self->{ context };
+
+ # generate table mapping object types to templates
+ my $map = $config->{ map } || { };
+ $map->{ default } = $config->{ default } unless defined $map->{ default };
+ $self->{ map } = {
+ %$MAP,
+ %$map,
+ };
+
+ # local BLOCKs definition table
+ $self->{ _BLOCKS } = $config->{ blocks } || { };
+
+ # name of presentation method which printed objects might provide
+ $self->{ method } = defined $config->{ method }
+ ? $config->{ method } : 'present';
+
+ # view is sealed by default preventing variable update after
+ # definition, however we don't actually seal a view until the
+ # END of the view definition
+ my $sealed = $config->{ sealed };
+ $sealed = 1 unless defined $sealed;
+ $self->{ sealed } = $sealed ? 1 : 0;
+
+ # copy remaining config items from $config or set defaults
+ foreach my $arg (qw( base prefix suffix notfound silent )) {
+ $self->{ $arg } = $config->{ $arg } || '';
+ }
+
+ # name of data item used by view()
+ $self->{ item } = $config->{ item } || 'item';
+
+ # map methods of form ${include_prefix}_foobar() to include('foobar')?
+ $self->{ include_prefix } = $config->{ include_prefix } || 'include_';
+ # what about mapping foobar() to include('foobar')?
+ $self->{ include_naked } = defined $config->{ include_naked }
+ ? $config->{ include_naked } : 1;
+
+ # map methods of form ${view_prefix}_foobar() to include('foobar')?
+ $self->{ view_prefix } = $config->{ view_prefix } || 'view_';
+ # what about mapping foobar() to view('foobar')?
+ $self->{ view_naked } = $config->{ view_naked } || 0;
+
+ # the view is initially unsealed, allowing directives in the initial
+ # view template to create data items via the AUTOLOAD; once sealed via
+ # call to seal(), the AUTOLOAD will not update any internal items.
+ delete @$config{ qw( base method map default prefix suffix notfound item
+ include_prefix include_naked silent sealed
+ view_prefix view_naked blocks ) };
+ $config = { %{ $self->{ base }->{ data } }, %$config }
+ if $self->{ base };
+ $self->{ data } = $config;
+ $self->{ SEALED } = 0;
+
+ return $self;
+}
+
+
+#------------------------------------------------------------------------
+# seal()
+# unseal()
+#
+# Seal or unseal the view to allow/prevent new datat items from being
+# automatically created by the AUTOLOAD method.
+#------------------------------------------------------------------------
+
+sub seal {
+ my $self = shift;
+ $self->{ SEALED } = $self->{ sealed };
+}
+
+sub unseal {
+ my $self = shift;
+ $self->{ SEALED } = 0;
+}
+
+
+#------------------------------------------------------------------------
+# clone(\%config)
+#
+# Cloning method which takes a copy of $self and then applies to it any
+# modifications specified in the $config hash passed as an argument.
+# Configuration items may also be specified as a list of "name => $value"
+# arguments. Returns a reference to the cloned Template::View object.
+#
+# NOTE: may need to copy BLOCKS???
+#------------------------------------------------------------------------
+
+sub clone {
+ my $self = shift;
+ my $clone = bless { %$self }, ref $self;
+ my $config = ref $_[0] eq 'HASH' ? shift : { @_ };
+
+ # merge maps
+ $clone->{ map } = {
+ %{ $self->{ map } },
+ %{ $config->{ map } || { } },
+ };
+
+ # "map => { default=>'xxx' }" can be specified as "default => 'xxx'"
+ $clone->{ map }->{ default } = $config->{ default }
+ if defined $config->{ default };
+
+ # update any remaining config items
+ my @args = qw( base prefix suffix notfound item method include_prefix
+ include_naked view_prefix view_naked );
+ foreach my $arg (@args) {
+ $clone->{ $arg } = $config->{ $arg } if defined $config->{ $arg };
+ }
+ push(@args, qw( default map ));
+ delete @$config{ @args };
+
+ # anything left is data
+ my $data = $clone->{ data } = { %{ $self->{ data } } };
+ @$data{ keys %$config } = values %$config;
+
+ return $clone;
+}
+
+
+#------------------------------------------------------------------------
+# print(@items, ..., \%config)
+#
+# Prints @items in turn by mapping each to an approriate template using
+# the internal 'map' hash. If an entry isn't found and the item is an
+# object that implements the method named in the internal 'method' item,
+# (default: 'present'), then the method will be called passing a reference
+# to $self, against which the presenter method may make callbacks (e.g.
+# to view_item()). If the presenter method isn't implemented, then the
+# 'default' map entry is consulted and used if defined. The final argument
+# may be a reference to a hash array providing local overrides to the internal
+# defaults for various items (prefix, suffix, etc). In the presence
+# of this parameter, a clone of the current object is first made, applying
+# any configuration updates, and control is then delegated to it.
+#------------------------------------------------------------------------
+
+sub print {
+ my $self = shift;
+
+ # if final config hash is specified then create a clone and delegate to it
+ # NOTE: potential problem when called print(\%data_hash1, \%data_hash2);
+ if ((scalar @_ > 1) && (ref $_[-1] eq 'HASH')) {
+ my $cfg = pop @_;
+ my $clone = $self->clone($cfg)
+ || return;
+ return $clone->print(@_)
+ || $self->error($clone->error());
+ }
+ my ($item, $type, $template, $present);
+ my $method = $self->{ method };
+ my $map = $self->{ map };
+ my $output = '';
+
+ # print each argument
+ foreach $item (@_) {
+ my $newtype;
+
+ if (! ($type = ref $item)) {
+ # non-references are TEXT
+ $type = 'TEXT';
+ $template = $map->{ $type };
+ }
+ elsif (! defined ($template = $map->{ $type })) {
+ # no specific map entry for object, maybe it implements a
+ # 'present' (or other) method?
+# $self->DEBUG("determining if $item can $method\n") if $DEBUG;
+ if ( $method && UNIVERSAL::can($item, $method) ) {
+ $self->DEBUG("Calling \$item->$method\n") if $DEBUG;
+ $present = $item->$method($self); ## call item method
+ # undef returned indicates error, note that we expect
+ # $item to have called error() on the view
+ return unless defined $present;
+ $output .= $present;
+ next; ## NEXT
+ }
+ elsif ( UNIVERSAL::isa($item, 'HASH' )
+ && defined($newtype = $item->{$method})
+ && defined($template = $map->{"$method=>$newtype"})) {
+ }
+ elsif ( defined($newtype)
+ && defined($template = $map->{"$method=>*"}) ) {
+ $template =~ s/\*/$newtype/;
+ }
+ elsif (! ($template = $map->{ default }) ) {
+ # default not defined, so construct template name from type
+ ($template = $type) =~ s/\W+/_/g;
+ }
+ }
+# else {
+# $self->DEBUG("defined map type for $type: $template\n");
+# }
+ $self->DEBUG("printing view '", $template || '', "', $item\n") if $DEBUG;
+ $output .= $self->view($template, $item)
+ if $template;
+ }
+ return $output;
+}
+
+
+#------------------------------------------------------------------------
+# view($template, $item, \%vars)
+#
+# Wrapper around include() which expects a template name, $template,
+# followed by a data item, $item, and optionally, a further hash array
+# of template variables. The $item is added as an entry to the $vars
+# hash (which is created empty if not passed as an argument) under the
+# name specified by the internal 'item' member, which is appropriately
+# 'item' by default. Thus an external object present() method can
+# callback against this object method, simply passing a data item to
+# be displayed. The external object doesn't have to know what the
+# view expects the item to be called in the $vars hash.
+#------------------------------------------------------------------------
+
+sub view {
+ my ($self, $template, $item) = splice(@_, 0, 3);
+ my $vars = ref $_[0] eq 'HASH' ? shift : { @_ };
+ $vars->{ $self->{ item } } = $item if defined $item;
+ $self->include($template, $vars);
+}
+
+
+#------------------------------------------------------------------------
+# include($template, \%vars)
+#
+# INCLUDE a template, $template, mapped according to the current prefix,
+# suffix, default, etc., where $vars is an optional hash reference
+# containing template variable definitions. If the template isn't found
+# then the method will default to any 'notfound' template, if defined
+# as an internal item.
+#------------------------------------------------------------------------
+
+sub include {
+ my ($self, $template, $vars) = @_;
+ my $context = $self->{ _CONTEXT };
+
+ $template = $self->template($template);
+
+ $vars = { } unless ref $vars eq 'HASH';
+ $vars->{ view } ||= $self;
+
+ $context->include( $template, $vars );
+
+# DEBUGGING
+# my $out = $context->include( $template, $vars );
+# print STDERR "VIEW return [$out]\n";
+# return $out;
+}
+
+
+#------------------------------------------------------------------------
+# template($template)
+#
+# Returns a compiled template for the specified template name, according
+# to the current configuration parameters.
+#------------------------------------------------------------------------
+
+sub template {
+ my ($self, $name) = @_;
+ my $context = $self->{ _CONTEXT };
+ return $context->throw(Template::Constants::ERROR_VIEW,
+ "no view template specified")
+ unless $name;
+
+ my $notfound = $self->{ notfound };
+ my $base = $self->{ base };
+ my ($template, $block, $error);
+
+ return $block
+ if ($block = $self->{ _BLOCKS }->{ $name });
+
+ # try the named template
+ $template = $self->template_name($name);
+ $self->DEBUG("looking for $template\n") if $DEBUG;
+ eval { $template = $context->template($template) };
+
+ # try asking the base view if not found
+ if (($error = $@) && $base) {
+ $self->DEBUG("asking base for $name\n") if $DEBUG;
+ eval { $template = $base->template($name) };
+ }
+
+ # try the 'notfound' template (if defined) if that failed
+ if (($error = $@) && $notfound) {
+ unless ($template = $self->{ _BLOCKS }->{ $notfound }) {
+ $notfound = $self->template_name($notfound);
+ $self->DEBUG("not found, looking for $notfound\n") if $DEBUG;
+ eval { $template = $context->template($notfound) };
+
+ return $context->throw(Template::Constants::ERROR_VIEW, $error)
+ if $@; # return first error
+ }
+ }
+ elsif ($error) {
+ $self->DEBUG("no 'notfound'\n")
+ if $DEBUG;
+ return $context->throw(Template::Constants::ERROR_VIEW, $error);
+ }
+ return $template;
+}
+
+
+#------------------------------------------------------------------------
+# template_name($template)
+#
+# Returns the name of the specified template with any appropriate prefix
+# and/or suffix added.
+#------------------------------------------------------------------------
+
+sub template_name {
+ my ($self, $template) = @_;
+ $template = $self->{ prefix } . $template . $self->{ suffix }
+ if $template;
+
+ $self->DEBUG("template name: $template\n") if $DEBUG;
+ return $template;
+}
+
+
+#------------------------------------------------------------------------
+# default($val)
+#
+# Special case accessor to retrieve/update 'default' as an alias for
+# '$map->{ default }'.
+#------------------------------------------------------------------------
+
+sub default {
+ my $self = shift;
+ return @_ ? ($self->{ map }->{ default } = shift)
+ : $self->{ map }->{ default };
+}
+
+
+#------------------------------------------------------------------------
+# AUTOLOAD
+#
+
+# Returns/updates public internal data items (i.e. not prefixed '_' or
+# '.') or presents a view if the method matches the view_prefix item,
+# e.g. view_foo(...) => view('foo', ...). Similarly, the
+# include_prefix is used, if defined, to map include_foo(...) to
+# include('foo', ...). If that fails then the entire method name will
+# be used as the name of a template to include iff the include_named
+# parameter is set (default: 1). Last attempt is to match the entire
+# method name to a view() call, iff view_naked is set. Otherwise, a
+# 'view' exception is raised reporting the error "no such view member:
+# $method".
+#------------------------------------------------------------------------
+
+sub AUTOLOAD {
+ my $self = shift;
+ my $item = $AUTOLOAD;
+ $item =~ s/.*:://;
+ return if $item eq 'DESTROY';
+
+ if ($item =~ /^[\._]/) {
+ return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
+ "attempt to view private member: $item");
+ }
+ elsif (exists $self->{ $item }) {
+ # update existing config item (e.g. 'prefix') if unsealed
+ return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
+ "cannot update config item in sealed view: $item")
+ if @_ && $self->{ SEALED };
+ $self->DEBUG("accessing item: $item\n") if $DEBUG;
+ return @_ ? ($self->{ $item } = shift) : $self->{ $item };
+ }
+ elsif (exists $self->{ data }->{ $item }) {
+ # get/update existing data item (must be unsealed to update)
+ if (@_ && $self->{ SEALED }) {
+ return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
+ "cannot update item in sealed view: $item")
+ unless $self->{ silent };
+ # ignore args if silent
+ @_ = ();
+ }
+ $self->DEBUG(@_ ? "updating data item: $item <= $_[0]\n"
+ : "returning data item: $item\n") if $DEBUG;
+ return @_ ? ($self->{ data }->{ $item } = shift)
+ : $self->{ data }->{ $item };
+ }
+ elsif (@_ && ! $self->{ SEALED }) {
+ # set data item if unsealed
+ $self->DEBUG("setting unsealed data: $item => @_\n") if $DEBUG;
+ $self->{ data }->{ $item } = shift;
+ }
+ elsif ($item =~ s/^$self->{ view_prefix }//) {
+ $self->DEBUG("returning view($item)\n") if $DEBUG;
+ return $self->view($item, @_);
+ }
+ elsif ($item =~ s/^$self->{ include_prefix }//) {
+ $self->DEBUG("returning include($item)\n") if $DEBUG;
+ return $self->include($item, @_);
+ }
+ elsif ($self->{ include_naked }) {
+ $self->DEBUG("returning naked include($item)\n") if $DEBUG;
+ return $self->include($item, @_);
+ }
+ elsif ($self->{ view_naked }) {
+ $self->DEBUG("returning naked view($item)\n") if $DEBUG;
+ return $self->view($item, @_);
+ }
+ else {
+ return $self->{ _CONTEXT }->throw(Template::Constants::ERROR_VIEW,
+ "no such view member: $item");
+ }
+}
+
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Template::View - customised view of a template processing context
+
+=head1 SYNOPSIS
+
+ # define a view
+ [% VIEW view
+ # some standard args
+ prefix => 'my_',
+ suffix => '.tt2',
+ notfound => 'no_such_file'
+ ...
+
+ # any other data
+ title => 'My View title'
+ other_item => 'Joe Random Data'
+ ...
+ %]
+ # add new data definitions, via 'my' self reference
+ [% my.author = "$abw.name <$abw.email>" %]
+ [% my.copy = "&copy; Copyright 2000 $my.author" %]
+
+ # define a local block
+ [% BLOCK header %]
+ This is the header block, title: [% title or my.title %]
+ [% END %]
+
+ [% END %]
+
+ # access data items for view
+ [% view.title %]
+ [% view.other_item %]
+
+ # access blocks directly ('include_naked' option, set by default)
+ [% view.header %]
+ [% view.header(title => 'New Title') %]
+
+ # non-local templates have prefix/suffix attached
+ [% view.footer %] # => [% INCLUDE my_footer.tt2 %]
+
+ # more verbose form of block access
+ [% view.include( 'header', title => 'The Header Title' ) %]
+ [% view.include_header( title => 'The Header Title' ) %]
+
+ # very short form of above ('include_naked' option, set by default)
+ [% view.header( title => 'The Header Title' ) %]
+
+ # non-local templates have prefix/suffix attached
+ [% view.footer %] # => [% INCLUDE my_footer.tt2 %]
+
+ # fallback on the 'notfound' template ('my_no_such_file.tt2')
+ # if template not found
+ [% view.include('missing') %]
+ [% view.include_missing %]
+ [% view.missing %]
+
+ # print() includes a template relevant to argument type
+ [% view.print("some text") %] # type=TEXT, template='text'
+
+ [% BLOCK my_text.tt2 %] # 'text' with prefix/suffix
+ Text: [% item %]
+ [% END %]
+
+ # now print() a hash ref, mapped to 'hash' template
+ [% view.print(some_hash_ref) %] # type=HASH, template='hash'
+
+ [% BLOCK my_hash.tt2 %] # 'hash' with prefix/suffix
+ hash keys: [% item.keys.sort.join(', ')
+ [% END %]
+
+ # now print() a list ref, mapped to 'list' template
+ [% view.print(my_list_ref) %] # type=ARRAY, template='list'
+
+ [% BLOCK my_list.tt2 %] # 'list' with prefix/suffix
+ list: [% item.join(', ') %]
+ [% END %]
+
+ # print() maps 'My::Object' to 'My_Object'
+ [% view.print(myobj) %]
+
+ [% BLOCK my_My_Object.tt2 %]
+ [% item.this %], [% item.that %]
+ [% END %]
+
+ # update mapping table
+ [% view.map.ARRAY = 'my_list_template' %]
+ [% view.map.TEXT = 'my_text_block' %]
+
+
+ # change prefix, suffix, item name, etc.
+ [% view.prefix = 'your_' %]
+ [% view.default = 'anyobj' %]
+ ...
+
+=head1 DESCRIPTION
+
+TODO
+
+=head1 METHODS
+
+=head2 new($context, \%config)
+
+Creates a new Template::View presenting a custom view of the specified
+$context object.
+
+A reference to a hash array of configuration options may be passed as the
+second argument.
+
+=over 4
+
+=item prefix
+
+Prefix added to all template names.
+
+ [% USE view(prefix => 'my_') %]
+ [% view.view('foo', a => 20) %] # => my_foo
+
+=item suffix
+
+Suffix added to all template names.
+
+ [% USE view(suffix => '.tt2') %]
+ [% view.view('foo', a => 20) %] # => foo.tt2
+
+=item map
+
+Hash array mapping reference types to template names. The print()
+method uses this to determine which template to use to present any
+particular item. The TEXT, HASH and ARRAY items default to 'test',
+'hash' and 'list' appropriately.
+
+ [% USE view(map => { ARRAY => 'my_list',
+ HASH => 'your_hash',
+ My::Foo => 'my_foo', } ) %]
+
+ [% view.print(some_text) %] # => text
+ [% view.print(a_list) %] # => my_list
+ [% view.print(a_hash) %] # => your_hash
+ [% view.print(a_foo) %] # => my_foo
+
+ [% BLOCK text %]
+ Text: [% item %]
+ [% END %]
+
+ [% BLOCK my_list %]
+ list: [% item.join(', ') %]
+ [% END %]
+
+ [% BLOCK your_hash %]
+ hash keys: [% item.keys.sort.join(', ')
+ [% END %]
+
+ [% BLOCK my_foo %]
+ Foo: [% item.this %], [% item.that %]
+ [% END %]
+
+=item method
+
+Name of a method which objects passed to print() may provide for presenting
+themselves to the view. If a specific map entry can't be found for an
+object reference and it supports the method (default: 'present') then
+the method will be called, passing the view as an argument. The object
+can then make callbacks against the view to present itself.
+
+ package Foo;
+
+ sub present {
+ my ($self, $view) = @_;
+ return "a regular view of a Foo\n";
+ }
+
+ sub debug {
+ my ($self, $view) = @_;
+ return "a debug view of a Foo\n";
+ }
+
+In a template:
+
+ [% USE view %]
+ [% view.print(my_foo_object) %] # a regular view of a Foo
+
+ [% USE view(method => 'debug') %]
+ [% view.print(my_foo_object) %] # a debug view of a Foo
+
+=item default
+
+Default template to use if no specific map entry is found for an item.
+
+ [% USE view(default => 'my_object') %]
+
+ [% view.print(objref) %] # => my_object
+
+If no map entry or default is provided then the view will attempt to
+construct a template name from the object class, substituting any
+sequence of non-word characters to single underscores, e.g.
+
+ # 'fubar' is an object of class Foo::Bar
+ [% view.print(fubar) %] # => Foo_Bar
+
+Any current prefix and suffix will be added to both the default template
+name and any name constructed from the object class.
+
+=item notfound
+
+Fallback template to use if any other isn't found.
+
+=item item
+
+Name of the template variable to which the print() method assigns the current
+item. Defaults to 'item'.
+
+ [% USE view %]
+ [% BLOCK list %]
+ [% item.join(', ') %]
+ [% END %]
+ [% view.print(a_list) %]
+
+ [% USE view(item => 'thing') %]
+ [% BLOCK list %]
+ [% thing.join(', ') %]
+ [% END %]
+ [% view.print(a_list) %]
+
+=item view_prefix
+
+Prefix of methods which should be mapped to view() by AUTOLOAD. Defaults
+to 'view_'.
+
+ [% USE view %]
+ [% view.view_header() %] # => view('header')
+
+ [% USE view(view_prefix => 'show_me_the_' %]
+ [% view.show_me_the_header() %] # => view('header')
+
+=item view_naked
+
+Flag to indcate if any attempt should be made to map method names to
+template names where they don't match the view_prefix. Defaults to 0.
+
+ [% USE view(view_naked => 1) %]
+
+ [% view.header() %] # => view('header')
+
+=back
+
+=head2 print( $obj1, $obj2, ... \%config)
+
+TODO
+
+=head2 view( $template, \%vars, \%config );
+
+TODO
+
+=head1 AUTHOR
+
+Andy Wardley E<lt>abw@kfs.orgE<gt>
+
+=head1 REVISION
+
+$Revision: 2.8 $
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000 Andy Wardley. All Rights Reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+=head1 SEE ALSO
+
+L<Template::Plugin|Template::Plugin>,
+
+=cut
+
+
+
+
+