Only in .: Makefile.old Only in .: RCS diff -cr ../HTML-Template-2.5-org/Template.pm ./Template.pm *** ../HTML-Template-2.5-org/Template.pm Fri Feb 1 18:01:37 2002 --- ./Template.pm Tue Jan 13 13:39:14 2004 *************** *** 1,6 **** package HTML::Template; ! $HTML::Template::VERSION = '2.5'; =head1 NAME --- 1,6 ---- package HTML::Template; ! $HTML::Template::VERSION = '2.5.4'; =head1 NAME *************** *** 47,53 **** This module attempts to make using HTML templates simple and natural. It extends standard HTML with a few new HTML-esque tags - , ! , , , and . The file written with HTML and these new tags is called a template. It is usually saved separate from your script - possibly even created by someone else! Using this module you fill in the values for the --- 47,53 ---- This module attempts to make using HTML templates simple and natural. It extends standard HTML with a few new HTML-esque tags - , ! , , , , and . The file written with HTML and these new tags is called a template. It is usually saved separate from your script - possibly even created by someone else! Using this module you fill in the values for the *************** *** 131,137 **** There is also the "ESCAPE=URL" option which may be used for VARs that populate a URL. It will do URL escaping, like replacing ' ' with '+' ! and '/' with '%2F'. =head2 TMPL_LOOP --- 131,138 ---- There is also the "ESCAPE=URL" option which may be used for VARs that populate a URL. It will do URL escaping, like replacing ' ' with '+' ! and '/' with '%2F'. The "ESCAPE=SQL" option can be used for VARs which ! are used in a SQL statement. =head2 TMPL_LOOP *************** *** 381,386 **** --- 382,446 ---- In order to realize a dramatic savings in bandwidth, the standard (non-comment) tags will be used throughout this documentation. + =head2 EXTENDED EXPRESSIONS + + Normally the template parameter is a variable name like or . But the parameter can also + be an expression. You can include comparisons, math and string operations + in the expression. Example: + + + I got for + dollars. + + gee, free pizza! + + + Any perl arithmetic and comparison operators can be used in an expression; even regular expressions. + So are the following functions: lc, lcfirst, uc, ucfirst, length, substr, index, + sprintf, rand, abs, time, int. All words not quoted are treated as + template variables. Please remember that you have to write '>' for + 'E', '<' for 'E' and '"' for '"' in your expressions, or + create a filter to convert them before passing to this package. For + example, I use #name# and #!expr!# notations for templates, and have a + simple filter which translates: + + #IF#!banana_count > 10 && name ne 'pizza'!# + I've got a lot of bananas and #name#. + #IF# + + into: + + + I've got a lot of bananas and . + + + Please note "EXPR=" and "NAME=" above are completely equivalent. + Also, the extended expressions are replaced by unique strings in the + template data structure; this makes it possible to save the data + in XML format for later use. + + =head2 DEFAULT VALUES & DEFINITIONS + + For TMPL_VAR, you can give a default value for the variable: + + + or + + + The default value will be used if the parameter's value is not defined. + + To define a parameter's value without generating any output, use the + TMPL_DEF tag: + + + + which you can use elsewhere in your template or perl scripts. + + To comment out any part of the template text so it won't appear in the + output, put it between ''. Note that any template + variables in there are still processed. + =head1 METHODS =head2 new() *************** *** 585,590 **** --- 645,655 ---- =item * + scalar_cache - if set the module will store/fetch its cache in the given + scalarref. + + =item * + double_file_cache - if set to 1 the module will use a combination of file_cache and normal cache mode for the best possible caching. The file_cache_* options that work with file_cache apply to double_file_cache *************** *** 715,753 **** =item * loop_context_vars - when this parameter is set to true (it is false by ! default) four loop context variables are made available inside a loop: ! __FIRST__, __LAST__, __INNER__, __ODD__. They can be used with , and to control how a loop is output. Example: ! This only outputs on the first pass. ! This outputs every other pass, on the odd passes. ! This outputs every other pass, on the even passes. ! This outputs on passes that are neither first nor last. ! This only outputs on the last pass. One use of this feature is to provide a "separator" similar in effect to the perl function join(). Example: ! and ! , . Would output (in a browser) something like: --- 780,822 ---- =item * loop_context_vars - when this parameter is set to true (it is false by ! default) five loop context variables are made available inside a loop: ! __first__, __last__, __inner__, __odd__, __counter__ and __count__. ! They can be used with , and to control how a loop is output. Example: ! This only outputs on the first pass. ! This outputs every other pass, on the odd passes. ! This outputs every other pass, on the even passes. ! This outputs on passes that are neither first nor last. ! This only outputs on the last pass. + + This is the -th record in the loop + with records. One use of this feature is to provide a "separator" similar in effect to the perl function join(). Example: ! and ! , . Would output (in a browser) something like: *************** *** 755,762 **** Apples, Oranges, Brains, Toes, and Kiwi. Given an appropriate param() call, of course. NOTE: A loop with only ! a single pass will get both __FIRST__ and __LAST__ set to true, but ! not __INNER__. =item * --- 824,831 ---- Apples, Oranges, Brains, Toes, and Kiwi. Given an appropriate param() call, of course. NOTE: A loop with only ! a single pass will get both __first__ and __last__ set to true, but ! not __inner__. =item * *************** *** 773,782 **** =item * ! global_vars - normally variables declared outside a loop are not ! available inside a loop. This option makes s like global ! variables in Perl - they have unlimited scope. This option also ! affects and . Example: --- 842,850 ---- =item * ! global_vars - normally variables declared outside a loop are not available ! inside a loop. This option makes s declared outside a loop ! available within the loop. Example: *************** *** 802,807 **** --- 870,889 ---- + Another way of accessing variables outside a loop, without turning on + global_vars, is to prefix the variable with ::. For example: + + This is a normal variable: .

+ + + Use it inside the loop:

+ + + =item * + + escape - a global option, if set, adds ESCAPE= to all s in + the template which don't have that option set. + =item * filter - this option allows you to specify a filter for your template *************** *** 859,865 **** =cut ! use integer; # no floating point math so far! use strict; # and no funny business, either. use Carp; # generate better errors with more context --- 941,947 ---- =cut ! #use integer; # no floating point math so far! not anymore use strict; # and no funny business, either. use Carp; # generate better errors with more context *************** *** 887,892 **** --- 969,975 ---- package HTML::Template; # open a new template and return an object handle + use vars qw(%DEF %FUNC); sub new { my $pkg = shift; my $self; { my %hash; $self = bless(\%hash, $pkg); } *************** *** 926,931 **** --- 1009,1015 ---- global_vars => 0, no_includes => 0, case_sensitive => 0, + escape => undef, filter => [], ); *************** *** 934,940 **** defined($_[($x + 1)]) or croak("HTML::Template->new() called with odd number of option parameters - should be of the form option => value"); $options->{lc($_[$x])} = $_[($x + 1)]; } ! # blind_cache = 1 implies cache = 1 $options->{blind_cache} and $options->{cache} = 1; --- 1018,1029 ---- defined($_[($x + 1)]) or croak("HTML::Template->new() called with odd number of option parameters - should be of the form option => value"); $options->{lc($_[$x])} = $_[($x + 1)]; } ! ! # additional associate array for ::name ! $options->{__associate}=[]; ! # __associate needs to be created ! $options->{__associateyes}=0; ! # blind_cache = 1 implies cache = 1 $options->{blind_cache} and $options->{cache} = 1; *************** *** 1026,1031 **** --- 1115,1130 ---- if ($@); } + if ($options->{scalar_cache}) { + croak("You must specify the scalar_cache option as a scalar ref.") + unless ref $options->{scalar_cache}; + + # file_cache needs some extra modules loaded + eval { require Storable; }; + croak("Could not load Storable. You must have Storable installed to use HTML::Template in scalar_cache mode. The error was: $@") + if ($@); + } + if ($options->{shared_cache}) { # shared_cache needs some extra modules loaded eval { require IPC::SharedCache; }; *************** *** 1044,1050 **** ipc_segment_size => $options->{ipc_segment_size}; $self->{cache} = \%cache; } ! print STDERR "### HTML::Template Memory Debug ### POST CACHE INIT ", $self->{proc_mem}->size(), "\n" if $options->{memory_debug}; --- 1143,1156 ---- ipc_segment_size => $options->{ipc_segment_size}; $self->{cache} = \%cache; } ! ! # empty the default cache ! #%DEF=(); # work? ! foreach(keys %DEF){ ! delete $DEF{$_}; ! } ! %FUNC=(); # work? ! print STDERR "### HTML::Template Memory Debug ### POST CACHE INIT ", $self->{proc_mem}->size(), "\n" if $options->{memory_debug}; *************** *** 1080,1085 **** --- 1186,1192 ---- stack_debug => 0, die_on_bad_params => 1, associate => [], + __associate => [], loop_context_vars => 0, ); *************** *** 1090,1097 **** --- 1197,1206 ---- } $self->{param_map} = $options->{param_map}; + $self->{exprs} = $options->{exprs}; $self->{parse_stack} = $options->{parse_stack}; delete($options->{param_map}); + delete($options->{exprs}); delete($options->{parse_stack}); return $self; *************** *** 1120,1168 **** if ($options->{double_cache}) { # try the normal cache, return if we have it. $self->_fetch_from_cache(); ! return if (defined $self->{param_map} and defined $self->{parse_stack}); # try the shared cache $self->_fetch_from_shared_cache(); # put it in the local cache if we got it. $self->_commit_to_cache() ! if (defined $self->{param_map} and defined $self->{parse_stack}); } elsif ($options->{double_file_cache}) { # try the normal cache, return if we have it. $self->_fetch_from_cache(); ! return if (defined $self->{param_map} and defined $self->{parse_stack}); # try the file cache $self->_fetch_from_file_cache(); # put it in the local cache if we got it. $self->_commit_to_cache() ! if (defined $self->{param_map} and defined $self->{parse_stack}); } elsif ($options->{shared_cache}) { # try the shared cache $self->_fetch_from_shared_cache(); } elsif ($options->{file_cache}) { # try the file cache $self->_fetch_from_file_cache(); } elsif ($options->{cache}) { # try the normal cache $self->_fetch_from_cache(); } # if we got a cache hit, return ! return if (defined $self->{param_map} and defined $self->{parse_stack}); ! # if we're here, then we didn't get a cached copy, so do a full # init. $self->_init_template(); $self->_parse(); # now that we have a full init, cache the structures if cacheing is # on. shared cache is already cool. if($options->{file_cache}){ $self->_commit_to_file_cache(); } $self->_commit_to_cache() if (($options->{cache} and not $options->{shared_cache} and not $options->{file_cache}) or --- 1229,1285 ---- if ($options->{double_cache}) { # try the normal cache, return if we have it. $self->_fetch_from_cache(); ! return if (defined $self->{param_map} and defined $self->{exprs} and defined $self->{parse_stack}); # try the shared cache $self->_fetch_from_shared_cache(); # put it in the local cache if we got it. $self->_commit_to_cache() ! if (defined $self->{param_map} and defined $self->{exprs} and defined $self->{parse_stack}); } elsif ($options->{double_file_cache}) { # try the normal cache, return if we have it. $self->_fetch_from_cache(); ! return if (defined $self->{param_map} and defined $self->{exprs} and defined $self->{parse_stack}); # try the file cache $self->_fetch_from_file_cache(); # put it in the local cache if we got it. $self->_commit_to_cache() ! if (defined $self->{param_map} and defined $self->{exprs} and defined $self->{parse_stack}); } elsif ($options->{shared_cache}) { # try the shared cache $self->_fetch_from_shared_cache(); } elsif ($options->{file_cache}) { # try the file cache $self->_fetch_from_file_cache(); + } elsif ($options->{scalar_cache}) { + # try the scalar_cache + $self->_fetch_from_scalar_cache(); } elsif ($options->{cache}) { # try the normal cache $self->_fetch_from_cache(); } # if we got a cache hit, return ! return if(defined $self->{param_map} and defined $self->{parse_stack} ! and defined $self->{exprs}); ! # if we're here, then we didn't get a cached copy, so do a full # init. $self->_init_template(); $self->_parse(); + #FacultyDatabase::dump('!!!!_parse'); # now that we have a full init, cache the structures if cacheing is # on. shared cache is already cool. if($options->{file_cache}){ $self->_commit_to_file_cache(); } + if($options->{scalar_cache}){ + $self->_commit_to_scalar_cache(); + } $self->_commit_to_cache() if (($options->{cache} and not $options->{shared_cache} and not $options->{file_cache}) or *************** *** 1221,1226 **** --- 1338,1344 ---- $options->{cache_debug} and print STDERR "### HTML::Template Cache Debug ### CACHE HIT : $filepath\n"; $self->{param_map} = $CACHE{$filepath}{param_map}; + $self->{exprs} = $CACHE{$filepath}{exprs}; $self->{parse_stack} = $CACHE{$filepath}{parse_stack}; exists($CACHE{$filepath}{included_mtimes}) and $self->{included_mtimes} = $CACHE{$filepath}{included_mtimes}; *************** *** 1247,1252 **** --- 1365,1371 ---- $options->{blind_cache} or $CACHE{$filepath}{mtime} = $self->_mtime($filepath); $CACHE{$filepath}{param_map} = $self->{param_map}; + $CACHE{$filepath}{exprs} = $self->{exprs}; $CACHE{$filepath}{parse_stack} = $self->{parse_stack}; exists($self->{included_mtimes}) and $CACHE{$filepath}{included_mtimes} = $self->{included_mtimes}; *************** *** 1294,1300 **** ($self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{parse_stack}) = @{$self->{record}}; $options->{filepath} = $filepath; --- 1413,1419 ---- ($self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{exprs}, $self->{parse_stack}) = @{$self->{record}}; $options->{filepath} = $filepath; *************** *** 1309,1316 **** print STDERR "### HTML::Template Cache Debug ### FILE CACHE MISS : $filepath : $mtime\n"; ($self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, ! $self->{parse_stack}) = (undef, undef, undef, undef); return; } --- 1428,1435 ---- print STDERR "### HTML::Template Cache Debug ### FILE CACHE MISS : $filepath : $mtime\n"; ($self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{exprs}, ! $self->{parse_stack}) = (undef, undef, undef, undef, undef); return; } *************** *** 1327,1334 **** print STDERR "### HTML::Template Cache Debug ### FILE CACHE MISS : $filepath : INCLUDE $filename : $included_mtime\n"; ($self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, ! $self->{parse_stack}) = (undef, undef, undef, undef); return; } } --- 1446,1453 ---- print STDERR "### HTML::Template Cache Debug ### FILE CACHE MISS : $filepath : INCLUDE $filename : $included_mtime\n"; ($self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{exprs}, ! $self->{parse_stack}) = (undef, undef, undef, undef, undef); return; } } *************** *** 1372,1378 **** eval { $result = Storable::lock_store([ $self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{parse_stack} ], scalar File::Spec->join($cache_dir, $cache_file) ); --- 1491,1497 ---- eval { $result = Storable::lock_store([ $self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{exprs}, $self->{parse_stack} ], scalar File::Spec->join($cache_dir, $cache_file) ); *************** *** 1383,1388 **** --- 1502,1534 ---- unless defined $result; } + # scalar cache + sub _fetch_from_scalar_cache { + my $self = shift; my $r=$self->{options}->{scalar_cache}; + return if !ref($r) || !$$r; + #FacultyDatabase::dump('!!!!_fetch_from_scalar_cache'); + + ($self->{param_map}, $self->{parse_stack}, + $self->{exprs}, $self->{options}->{__associateyes}) = @{Storable::thaw($$r)}; + + # clear out values from param_map from last run + $self->_normalize_options(); + $self->clear_params(); + } + + sub _commit_to_scalar_cache { + my $self = shift; my $r=$self->{options}->{scalar_cache}; + return if !ref($r) || $$r; + #FacultyDatabase::dump('!!!!_commit_to_scalar_cache'); + + eval{ + $$r=Storable::freeze([$self->{param_map}, $self->{parse_stack}, + $self->{exprs}, $self->{options}->{__associateyes}]); + }; + croak("HTML::Template::new() - Problem _commit_to_scalar_cache : $@") + if $@; + } + # Shared cache routines. sub _fetch_from_shared_cache { my $self = shift; *************** *** 1396,1402 **** ($self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{parse_stack}) = @{$self->{record}} if defined($self->{record}); --- 1542,1548 ---- ($self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{exprs}, $self->{parse_stack}) = @{$self->{record}} if defined($self->{record}); *************** *** 1417,1423 **** return 1 if $options->{blind_cache}; ! my ($c_mtime, $included_mtimes, $param_map, $parse_stack) = @$record; # if the modification time has changed return false my $mtime = $self->_mtime($filename); --- 1563,1569 ---- return 1 if $options->{blind_cache}; ! my ($c_mtime, $included_mtimes, $param_map, $exprs, $parse_stack) = @$record; # if the modification time has changed return false my $mtime = $self->_mtime($filename); *************** *** 1460,1466 **** return [ $self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{parse_stack} ]; } --- 1606,1612 ---- return [ $self->{mtime}, $self->{included_mtimes}, ! $self->{param_map}, $self->{exprs}, $self->{parse_stack} ]; } *************** *** 1658,1665 **** # setup the stacks and maps - they're accessed by typeglobs that # reference the top of the stack. They are masked so that a loop # can transparently have its own versions. ! use vars qw(@pstack %pmap @ifstack @ucstack %top_pmap); ! local (*pstack, *ifstack, *pmap, *ucstack, *top_pmap); # the pstack is the array of scalar refs (plain text from the # template file), VARs, LOOPs, IFs and ELSEs that output() works on --- 1804,1811 ---- # setup the stacks and maps - they're accessed by typeglobs that # reference the top of the stack. They are masked so that a loop # can transparently have its own versions. ! use vars qw(@pstack %pmap @ifstack @elsifstack @ucstack %expr %top_pmap); ! local (*pstack, *ifstack, *elsifstack, *pmap, *ucstack, *expr, *top_pmap); # the pstack is the array of scalar refs (plain text from the # template file), VARs, LOOPs, IFs and ELSEs that output() works on *************** *** 1674,1687 **** # pmap at all! my @pmaps = ({}); *pmap = $pmaps[0]; - *top_pmap = $pmaps[0]; $self->{param_map} = $pmaps[0]; # the ifstack is a temporary stack containing pending ifs and elses # waiting for a /if. my @ifstacks = ([]); *ifstack = $ifstacks[0]; # the ucstack is a temporary stack containing conditions that need # to be bound to param_map entries when their block is finished. # This happens when a conditional is encountered before any other --- 1820,1844 ---- # pmap at all! my @pmaps = ({}); *pmap = $pmaps[0]; $self->{param_map} = $pmaps[0]; + $pmap{0}=HTML::Template::VAR->new(); # default: some default set, value: expr used + *top_pmap = $pmaps[0]; + + # hold names of extended exprs and ::name (lazy) + my @exprs = ({}); + *expr = $exprs[0]; + $self->{exprs} = $exprs[0]; # the ifstack is a temporary stack containing pending ifs and elses # waiting for a /if. my @ifstacks = ([]); *ifstack = $ifstacks[0]; + # the elsifstack is a temporary stack for containing the elsif, + # which in reality expands/unrolls to become IF-ELSE-/IF. + my @elsifstacks = ([]); + *elsifstack = $elsifstacks[0]; + # the ucstack is a temporary stack containing conditions that need # to be bound to param_map entries when their block is finished. # This happens when a conditional is encountered before any other *************** *** 1711,1723 **** my $NOOP = HTML::Template::NOOP->new(); my $ESCAPE = HTML::Template::ESCAPE->new(); my $URLESCAPE = HTML::Template::URLESCAPE->new(); # all the tags that need NAMEs: my %need_names = map { $_ => 1 } ! qw(TMPL_VAR TMPL_LOOP TMPL_IF TMPL_UNLESS TMPL_INCLUDE); # variables used below that don't need to be my'd in the loop ! my ($name, $which, $escape); # handle the old vanguard format $options->{vanguard_compatibility_mode} and --- 1868,1881 ---- my $NOOP = HTML::Template::NOOP->new(); my $ESCAPE = HTML::Template::ESCAPE->new(); my $URLESCAPE = HTML::Template::URLESCAPE->new(); + my $SQLESCAPE = HTML::Template::SQLESCAPE->new(); # all the tags that need NAMEs: my %need_names = map { $_ => 1 } ! qw(TMPL_VAR TMPL_LOOP TMPL_IF TMPL_ELSIF TMPL_UNLESS TMPL_INCLUDE); # variables used below that don't need to be my'd in the loop ! my ($name, $value, $which, $escape); # handle the old vanguard format $options->{vanguard_compatibility_mode} and *************** *** 1751,1758 **** --- 1909,1920 ---- | (?:[Ee][Ll][Ss][Ee]) | + (?:[Ee][Ll][Ss][Ii][Ff]) + | (?:[Uu][Nn][Ll][Ee][Ss][Ss]) | + (?:[Dd][Ee][Ff]) + | (?:[Ii][Nn][Cc][Ll][Uu][Dd][Ee]) ) ) # $1 => $which - start of the tag *************** *** 1767,1778 **** ( 0 | (?:"0") | (?:'0') ) # $2 => ESCAPE off | ( 1 | (?:"1") | (?:'1') | ! (?:[Hh][Tt][Mm][Ll]) | ! (?:"[Hh][Tt][Mm][Ll]") | ! (?:'[Hh][Tt][Mm][Ll]') | ! (?:[Uu][Rr][Ll]) | ! (?:"[Uu][Rr][Ll]") | ! (?:'[Uu][Rr][Ll]') | ) # $3 => ESCAPE on ) )* # allow multiple ESCAPEs --- 1929,1935 ---- ( 0 | (?:"0") | (?:'0') ) # $2 => ESCAPE off | ( 1 | (?:"1") | (?:'1') | ! (?:\w+) | (?:"\w+") | (?:'\w+') ) # $3 => ESCAPE on ) )* # allow multiple ESCAPEs *************** *** 1782,1789 **** # NAME attribute (?: (?: ! [Nn][Aa][Mm][Ee] ! \s*=\s* )? (?: "([^">]*)" # $4 => double-quoted NAME value " --- 1939,1946 ---- # NAME attribute (?: (?: ! (?: [Nn][Aa][Mm][Ee] | [Ee][Xx][Pp][Rr] ) ! \s*=\s* )? (?: "([^">]*)" # $4 => double-quoted NAME value " *************** *** 1793,1798 **** --- 1950,1972 ---- ([^\s=>]*) # $6 => unquoted NAME value ) )? + + \s* + + # Value and Default attribute + (?: + (?: + (?: [Vv][Aa][Ll][Uu][Ee] | [Dd][Ee][Ff][Aa][Uu][Ll][Tt] ) + \s*=\s* + ) + (?: + "([^">]*)" # $7 => double-quoted NAME value " + | + '([^'>]*)' # $8 => single-quoted NAME value + | + ([^\s=>]*) # $9 => unquoted NAME value + ) + )? \s* *************** *** 1801,1830 **** [Ee][Ss][Cc][Aa][Pp][Ee] \s*=\s* (?: ! ( 0 | (?:"0") | (?:'0') ) # $7 => ESCAPE off | ( 1 | (?:"1") | (?:'1') | ! (?:[Hh][Tt][Mm][Ll]) | ! (?:"[Hh][Tt][Mm][Ll]") | ! (?:'[Hh][Tt][Mm][Ll]') | ! (?:[Uu][Rr][Ll]) | ! (?:"[Uu][Rr][Ll]") | ! (?:'[Uu][Rr][Ll]') | ! ) # $8 => ESCAPE on ) )* # allow multiple ESCAPEs \s* (?:--)?> ! (.*) # $9 => $post - text that comes after the tag $/sx) { $which = uc($1); # which tag is it ! $escape = $3 || $8; ! $escape = 0 if $2 || $7; # ESCAPE=0 $escape = 0 unless defined($escape); # what name for the tag? undef for a /tag at most, one of the # following three will be defined --- 1975,2003 ---- [Ee][Ss][Cc][Aa][Pp][Ee] \s*=\s* (?: ! ( 0 | (?:"0") | (?:'0') ) # $10 => ESCAPE off | ( 1 | (?:"1") | (?:'1') | ! (?:\w+) | (?:"\w+") | (?:'\w+') ! ) # $11 => ESCAPE on ) )* # allow multiple ESCAPEs \s* (?:--)?> ! (.*) # $12 => $post - text that comes after the tag $/sx) { $which = uc($1); # which tag is it + #$which = $1; $which = uc($which); # UTF-8 bug? ! $escape = $3 || $11; ! $escape = 0 if $2 || $10; # ESCAPE=0 $escape = 0 unless defined($escape); + if($options->{escape} and not $escape and $which eq 'TMPL_VAR'){# apply global escape if none is defined for this var + $escape=$options->{escape}; + } # what name for the tag? undef for a /tag at most, one of the # following three will be defined *************** *** 1832,1849 **** $name = $4 if defined($4); $name = $5 if defined($5); $name = $6 if defined($6); # allow mixed case in filenames, otherwise flatten $name = lc($name) unless ($which eq 'TMPL_INCLUDE' or $options->{case_sensitive}); ! my $post = $9; # what comes after on the line # die if we need a name and didn't get one die "HTML::Template->new() : No NAME given to a $which tag at $fname : line $fcounter." if (!defined($name) and $need_names{$which}); # die if we got an escape but can't use one die "HTML::Template->new() : ESCAPE option invalid in a $which tag at $fname : line $fcounter." if ( $escape and ($which ne 'TMPL_VAR')); ! # take actions depending on which tag found if ($which eq 'TMPL_VAR') { $options->{debug} and print STDERR "### HTML::Template Debug ### $fname : line $fcounter : parsed VAR $name\n"; --- 2005,2113 ---- $name = $4 if defined($4); $name = $5 if defined($5); $name = $6 if defined($6); + undef $value; + $value = $7 if defined($7); + $value = $8 if defined($8); + $value = $9 if defined($9); # allow mixed case in filenames, otherwise flatten $name = lc($name) unless ($which eq 'TMPL_INCLUDE' or $options->{case_sensitive}); ! my $post = $12; # what comes after on the line # die if we need a name and didn't get one die "HTML::Template->new() : No NAME given to a $which tag at $fname : line $fcounter." if (!defined($name) and $need_names{$which}); # die if we got an escape but can't use one die "HTML::Template->new() : ESCAPE option invalid in a $which tag at $fname : line $fcounter." if ( $escape and ($which ne 'TMPL_VAR')); ! ! if($name =~ /[^:\w\.\/+\-]/s){# parse and security check expr, no need to cache ! my $param=$name; ! my $fs="\x8d"; # a non \w ! my $i=1; my(%fs,$p,$q); # hold quoted strings ! die "HTML::Template: bad char found in an extended expr." if $param=~/($fs|\xff|\0)/s; ! ! $param=~s/>/>/g; ! $param=~s/</\|,=\!+\-\*\/%\.]+)/s; ! ! $param=~s,#(["rfFbp])(\d+)$fs, $p=$1; $i=$2; # subs all back with right quotes ! if($p eq '"'){# subs all back with right double quotes ! '"'.$fs{$i}.'"'; ! }elsif($p eq 'f' || $p eq 'b' || $p eq 'r'){ ! $fs{$i}; ! }elsif($p eq 'F'){# just mark it for output use ! $fs.$fs{$i}; ! }elsif($p eq 'p'){ ! $q=$fs{$i}; # add any new variables referenced in an expression ! if(!exists $pmap{$q}){ ! $pmap{$q}=HTML::Template::VAR->new(); ! if($q =~ /^::(.*)/ && !$expr{$q}){ ! $top_pmap{$1} = HTML::Template::VAR->new() if not exists $top_pmap{$1}; ! $options->{__associateyes}=1; ! $expr{$q}=1; # 1? ! } ! } ! "#$q$fs"; ! } ! ,seg; ! {# subs $,@,% outside single quotes, see below \xf. ! # keep them around inside single quotes for things like sprintf('%2f'...) ! $param=~s,\$,\x90,sg; ! $param=~s,\@,\x9d,sg; ! #$param=~s,\%,\x8f,sg; ! } ! $param=~s,#'(\d+)$fs,'$fs{$1}',sg; # subs all back with right single quotes ! $param=~s,#d1$fs,\\",sg; $param=~s,#d2$fs,\\',sg; # subs all back with right escaped quotes ! $param=~s,#c(\d+)$fs,q{$fs{$1}},sg; # put back trusted strings ! ! require Digest::MD5; # so we can dump data structure to XML ! $name='MD5'.Digest::MD5::md5_hex($param); #print STDERR "=f====== $param -> $name ====\n"; ! $expr{$name}=$param; # 1? ! }elsif($name =~ /^::(.*)/){# turn on __associateyes for top level if needed ! $top_pmap{$1} = HTML::Template::VAR->new() if not exists $top_pmap{$1}; ! $options->{__associateyes}=1; ! $expr{$name}=1; # 1? ! } ! # take actions depending on which tag found if ($which eq 'TMPL_VAR') { $options->{debug} and print STDERR "### HTML::Template Debug ### $fname : line $fcounter : parsed VAR $name\n"; *************** *** 1858,1865 **** } else { $var = HTML::Template::VAR->new(); $pmap{$name} = $var; ! $top_pmap{$name} = HTML::Template::VAR->new() ! if $options->{global_vars} and not exists $top_pmap{$name}; } # if ESCAPE was set, push an ESCAPE op on the stack before --- 2122,2131 ---- } else { $var = HTML::Template::VAR->new(); $pmap{$name} = $var; ! } ! if(defined($value)){# set default value ! my $noop=HTML::Template::NOOP->new($value); ! push(@pstack, $noop); } # if ESCAPE was set, push an ESCAPE op on the stack before *************** *** 1867,1872 **** --- 2133,2140 ---- if ($escape) { if ($escape =~ /^"?[Uu][Rr][Ll]"?$/) { push(@pstack, $URLESCAPE); + } elsif ($escape =~ /^"?[Ss][Qq][Ll]"?$/){ + push(@pstack, $SQLESCAPE); } else { push(@pstack, $ESCAPE); } *************** *** 1903,1910 **** --- 2171,2182 ---- *pstack = $pstacks[$#pstacks]; push(@pmaps, {}); *pmap = $pmaps[$#pmaps]; + push(@exprs, {}); + *expr = $exprs[$#exprs]; push(@ifstacks, []); *ifstack = $ifstacks[$#ifstacks]; + push(@elsifstacks, []); + *elsifstack = $elsifstacks[$#elsifstacks]; push(@ucstacks, []); *ucstack = $ucstacks[$#ucstacks]; *************** *** 1917,1922 **** --- 2189,2196 ---- $pmap{__inner__} = HTML::Template::VAR->new(); $pmap{__last__} = HTML::Template::VAR->new(); $pmap{__odd__} = HTML::Template::VAR->new(); + $pmap{__counter__} = HTML::Template::VAR->new(); + $pmap{__count__} = HTML::Template::VAR->new(); } } elsif ($which eq '/TMPL_LOOP') { *************** *** 1934,1941 **** $uc->[HTML::Template::COND::VARIABLE] = $pmap{$var}; } else { $pmap{$var} = HTML::Template::VAR->new(); - $top_pmap{$var} = HTML::Template::VAR->new() - if $options->{global_vars} and not exists $top_pmap{$var}; $uc->[HTML::Template::COND::VARIABLE] = $pmap{$var}; } if (ref($pmap{$var}) eq 'HTML::Template::VAR') { --- 2208,2213 ---- *************** *** 1949,1960 **** --- 2221,2236 ---- # the enclosing block. my $param_map = pop(@pmaps); *pmap = $pmaps[$#pmaps]; + my $exprs = pop(@exprs); + *expr = $exprs[$#exprs]; my $parse_stack = pop(@pstacks); *pstack = $pstacks[$#pstacks]; scalar(@ifstack) and die "HTML::Template->new() : Dangling or in loop ending at $fname : line $fcounter."; pop(@ifstacks); *ifstack = $ifstacks[$#ifstacks]; + pop(@elsifstacks); + *elsifstack = $elsifstacks[$#elsifstacks]; pop(@ucstacks); *ucstack = $ucstacks[$#ucstacks]; *************** *** 1966,1976 **** = HTML::Template->_new_from_loop( parse_stack => $parse_stack, param_map => $param_map, debug => $options->{debug}, die_on_bad_params => $options->{die_on_bad_params}, loop_context_vars => $options->{loop_context_vars}, ! case_sensitive => $options->{case_sensitive}, ! ); } elsif ($which eq 'TMPL_IF' or $which eq 'TMPL_UNLESS' ) { $options->{debug} and print STDERR "### HTML::Template Debug ### $fname : line $fcounter : $which $name start\n"; --- 2242,2253 ---- = HTML::Template->_new_from_loop( parse_stack => $parse_stack, param_map => $param_map, + exprs => $exprs, debug => $options->{debug}, die_on_bad_params => $options->{die_on_bad_params}, loop_context_vars => $options->{loop_context_vars}, ! case_sensitive => $options->{case_sensitive}, ! ); } elsif ($which eq 'TMPL_IF' or $which eq 'TMPL_UNLESS' ) { $options->{debug} and print STDERR "### HTML::Template Debug ### $fname : line $fcounter : $which $name start\n"; *************** *** 2009,2019 **** # push what we've got onto the stacks push(@pstack, $cond); push(@ifstack, $cond); ! } elsif ($which eq '/TMPL_IF' or $which eq '/TMPL_UNLESS') { ! $options->{debug} and print STDERR "### HTML::Template Debug ###$fname : line $fcounter : $which end\n"; my $cond = pop(@ifstack); die "HTML::Template->new() : found with no matching at $fname : line $fcounter." unless defined $cond; if ($which eq '/TMPL_IF') { die "HTML::Template->new() : found incorrectly terminating a (use ) at $fname : line $fcounter.\n" --- 2286,2362 ---- # push what we've got onto the stacks push(@pstack, $cond); push(@ifstack, $cond); + # create a new stack for any possible ELSIF at this level: + push(@elsifstacks, []); + *elsifstack = $elsifstacks[$#elsifstacks]; ! } elsif ($which eq 'TMPL_ELSIF') { ! $options->{debug} and print STDERR "### HTML::Template Debug ### $fname : line $fcounter : ELSIF\n"; my $cond = pop(@ifstack); + die "HTML::Template->new() : found with no matching at $fname : line $fcounter." unless defined $cond; + die "HTML::Template->new() : found encountered without or [HTML::Template::COND::WHICH] == HTML::Template::COND::WHICH_IF); + + my $else = HTML::Template::COND->new($cond->[HTML::Template::COND::VARIABLE]); + $else->[HTML::Template::COND::WHICH] = $cond->[HTML::Template::COND::WHICH]; + $else->[HTML::Template::COND::JUMP_IF_TRUE] = not $cond->[HTML::Template::COND::JUMP_IF_TRUE]; + + # need end-block resolution? + if (defined($cond->[HTML::Template::COND::VARIABLE_TYPE])) { + $else->[HTML::Template::COND::VARIABLE_TYPE] = $cond->[HTML::Template::COND::VARIABLE_TYPE]; + } else { + push(@ucstack, $else); + } + + push(@pstack, $else); + push(@ifstack, $else); + + # connect the matching to this "address" - thus the IF, + # failing jumps to the ELSE address. The else then gets + # elaborated, and of course succeeds. On the other hand, if + # the IF fails and falls though, output will reach the else + # and jump to the /IF address. + $cond->[HTML::Template::COND::JUMP_ADDRESS] = $#pstack; + + ## Copied from TMPL_IF above: + # if we already have this var, then simply link to the existing + # HTML::Template::VAR/LOOP, else defer the mapping + my $var; + if (exists $pmap{$name}) { + $var = $pmap{$name}; + } else { + $var = $name; + } + + # treat elsif as an if, for the jump condition + my $cond_if = HTML::Template::COND->new($var); + $cond_if->[HTML::Template::COND::WHICH] = HTML::Template::COND::WHICH_IF; + $cond_if->[HTML::Template::COND::JUMP_IF_TRUE] = 0; + + # push unconnected conditionals onto the ucstack for + # resolution later. Otherwise, save type information now. + if ($var eq $name) { + push(@ucstack, $cond_if); + } else { + if (ref($var) eq 'HTML::Template::VAR') { + $cond_if->[HTML::Template::COND::VARIABLE_TYPE] = HTML::Template::COND::VARIABLE_TYPE_VAR; + } else { + $cond_if->[HTML::Template::COND::VARIABLE_TYPE] = HTML::Template::COND::VARIABLE_TYPE_LOOP; + } + } + + # push what we've got onto the stacks + push(@pstack, $cond_if); + push(@ifstack, $cond_if); + push(@elsifstack, $cond_if); + + } elsif ($which eq '/TMPL_IF' or $which eq '/TMPL_UNLESS') { + $options->{debug} and print STDERR "### HTML::Template Debug ###$fname : line $fcounter : $which end\n"; + + do{ + #FacultyDatabase::dump('pop',@elsifstack); + my $cond = pop(@ifstack); die "HTML::Template->new() : found with no matching at $fname : line $fcounter." unless defined $cond; if ($which eq '/TMPL_IF') { die "HTML::Template->new() : found incorrectly terminating a (use ) at $fname : line $fcounter.\n" *************** *** 2028,2035 **** # assembler-esque "Conditional Jump" mode. push(@pstack, $NOOP); $cond->[HTML::Template::COND::JUMP_ADDRESS] = $#pstack; ! ! } elsif ($which eq 'TMPL_ELSE') { $options->{debug} and print STDERR "### HTML::Template Debug ### $fname : line $fcounter : ELSE\n"; my $cond = pop(@ifstack); --- 2371,2382 ---- # assembler-esque "Conditional Jump" mode. push(@pstack, $NOOP); $cond->[HTML::Template::COND::JUMP_ADDRESS] = $#pstack; ! }while(pop @elsifstack); # unroll the ELSIF stack ! ! pop(@elsifstacks); ! *elsifstack = $elsifstacks[$#elsifstacks]; ! ! } elsif ($which eq 'TMPL_ELSE') { $options->{debug} and print STDERR "### HTML::Template Debug ### $fname : line $fcounter : ELSE\n"; my $cond = pop(@ifstack); *************** *** 2125,2130 **** --- 2472,2501 ---- redo CHUNK; } + + }elsif ($which eq 'TMPL_DEF') { + $options->{debug} and print STDERR "### HTML::Template Debug ### $fname : line $fcounter : parsed DEF $name\n"; + + # if we already have this var, then simply link to the existing + # HTML::Template::VAR, else create a new one. + my $var; + if (exists $pmap{$name}) { + $var = $pmap{$name}; + (ref($var) eq 'HTML::Template::VAR') or + die "HTML::Template->new() : Already used param name $name as a TMPL_LOOP, found in a TMPL_VAR at $fname : line $fcounter."; + } else { + $var = HTML::Template::VAR->new(); + $pmap{$name} = $var; + } + if(defined($value)){# set default value + $value=~s/>/>/g; + $value=~s/</new() : Unknown or unmatched TMPL construct at $fname : line $fcounter."; *************** *** 2158,2164 **** # pop off the record and re-alias to the enclosing file's info pop(@fstack), (*fname, *fcounter, *fmax) = \ ( @{$fstack[$#fstack]} ) if ($fcounter > $fmax); ! } # next CHUNK # make sure we don't have dangling IF or LOOP blocks --- 2529,2536 ---- # pop off the record and re-alias to the enclosing file's info pop(@fstack), (*fname, *fcounter, *fmax) = \ ( @{$fstack[$#fstack]} ) if ($fcounter > $fmax); ! ! #FacultyDatabase::dump('which',$which,$name,'ifstacks',@ifstacks,'elsifstacks',@elsifstacks); } # next CHUNK # make sure we don't have dangling IF or LOOP blocks *************** *** 2172,2179 **** $uc->[HTML::Template::COND::VARIABLE] = $pmap{$var}; } else { $pmap{$var} = HTML::Template::VAR->new(); - $top_pmap{$var} = HTML::Template::VAR->new() - if $options->{global_vars} and not exists $top_pmap{$var}; $uc->[HTML::Template::COND::VARIABLE] = $pmap{$var}; } if (ref($pmap{$var}) eq 'HTML::Template::VAR') { --- 2544,2549 ---- *************** *** 2194,2199 **** --- 2564,2582 ---- delete $options->{filter}; } + sub register_function {# name fullname + my($class, $name, $sub) = @_; + + if(!$sub){# fix it as best as we can + my $p=(caller())[0]; $p='' if !$p; $p.='::' if $p; + $sub='&'.$p.$name; + }elsif(ref($sub) eq 'CODE'){ + croak("HTML::Template : args 3 of register_function must be subroutine name"); + } + + $FUNC{$name} = $sub; + } + # a recursive sub that associates each loop with the loops above # (treating the top-level as a loop) sub _globalize_vars { *************** *** 2209,2214 **** --- 2592,2610 ---- map {values %{$_->[HTML::Template::LOOP::TEMPLATE_HASH]}} grep { ref($_) eq 'HTML::Template::LOOP'} @{$self->{parse_stack}}; } + sub _globalize__associate { + my $self = shift; + + # associate with the loop (and top-level templates) above in the tree. + push(@{$self->{options}{__associate}}, @_); + + # recurse down into the template tree, adding ourself to the end of + # list first. + push(@_, $self); + map { $_->_globalize__associate(@_) } + map {values %{$_->[HTML::Template::LOOP::TEMPLATE_HASH]}} + grep { ref($_) eq 'HTML::Template::LOOP'} @{$self->{parse_stack}}; + } # method used to recursively un-hook associate sub _unglobalize_vars { *************** *** 2222,2227 **** --- 2618,2634 ---- map {values %{$_->[HTML::Template::LOOP::TEMPLATE_HASH]}} grep { ref($_) eq 'HTML::Template::LOOP'} @{$self->{parse_stack}}; } + sub _unglobalize__associate { + my $self = shift; + + # disassociate + $self->{options}{__associate} = undef; + + # recurse down into the template tree disassociating + map { $_->_unglobalize__associate() } + map {values %{$_->[HTML::Template::LOOP::TEMPLATE_HASH]}} + grep { ref($_) eq 'HTML::Template::LOOP'} @{$self->{parse_stack}}; + } =head2 param() *************** *** 2300,2305 **** --- 2707,2727 ---- } ); + =head2 register_function() + + For security reason, not all functions are allowed to be executed in + an extended expression. But register_function lets you specify the functions + which can be run from the template. Example: + + $tmpl->register_function('SQL','&MyPackage::SQLexec'); + + and it could be used in the template in such a way: + + #LOOP#!SQL:select * from News where Date>=NOW()!# +

  • #Source#: #Mesg#
  • + #LOOP# + + =cut *************** *** 2360,2370 **** # figure out what we've got, taking special care to allow for # objects that are compatible underneath. ! my $value_type = ref($value); if (defined($value_type) and length($value_type) and ($value_type eq 'ARRAY' or ((ref($value) !~ /^(CODE)|(HASH)|(SCALAR)$/) and $value->isa('ARRAY')))) { ! (ref($param_map->{$param}) eq 'HTML::Template::LOOP') or croak("HTML::Template::param() : attempt to set parameter '$param' with an array ref - parameter is not a TMPL_LOOP!"); ! $param_map->{$param}[HTML::Template::LOOP::PARAM_SET] = [@{$value}]; } else { (ref($param_map->{$param}) eq 'HTML::Template::VAR') or croak("HTML::Template::param() : attempt to set parameter '$param' with a scalar - parameter is not a TMPL_VAR!"); --- 2782,2798 ---- # figure out what we've got, taking special care to allow for # objects that are compatible underneath. ! my $value_type = ref($value); #main::dump($param,'=====',$value); if (defined($value_type) and length($value_type) and ($value_type eq 'ARRAY' or ((ref($value) !~ /^(CODE)|(HASH)|(SCALAR)$/) and $value->isa('ARRAY')))) { ! if(ref($param_map->{$param}) eq 'HTML::Template::LOOP'){ ! $param_map->{$param}[HTML::Template::LOOP::PARAM_SET] = [@{$value}]; ! }elsif(ref($param_map->{$param}) eq 'HTML::Template::VAR'){# accidently set a arrayref to a VAR, just count the element numbers ! ${$param_map->{$param}} = scalar(@{$value}); ! }else{ croak("HTML::Template::param() : attempt to set parameter '$param' with an array ref - parameter is not a TMPL_LOOP!"); ! } ! }elsif(ref($param_map->{$param}) eq 'HTML::Template::LOOP'){# deal with XML::Simple turns an array with a single element into that element ! $param_map->{$param}[HTML::Template::LOOP::PARAM_SET] = [$value]; } else { (ref($param_map->{$param}) eq 'HTML::Template::VAR') or croak("HTML::Template::param() : attempt to set parameter '$param' with a scalar - parameter is not a TMPL_VAR!"); *************** *** 2393,2399 **** } } - # obsolete implementation of associate sub associateCGI { my $self = shift; --- 2821,2826 ---- *************** *** 2431,2439 **** =cut ! use vars qw(%URLESCAPE_MAP); sub output { ! my $self = shift; my $options = $self->{options}; croak("HTML::Template->output() : You gave me an odd number of parameters to output()!") --- 2858,2866 ---- =cut ! use vars qw(%URLESCAPE_MAP $SELF); sub output { ! my $self = shift; local $SELF=$self; # a reference to the template itself my $options = $self->{options}; croak("HTML::Template->output() : You gave me an odd number of parameters to output()!") *************** *** 2454,2463 **** # globalize vars - this happens here to localize the circular # references created by global_vars. $self->_globalize_vars() if ($options->{global_vars}); # support the associate magic, searching for undefined params and # attempting to fill them from the associated objects. ! if (scalar(@{$options->{associate}})) { # prepare case-mapping hashes to do case-insensitive matching # against associated objects. This allows CGI.pm to be # case-sensitive and still work with asssociate. --- 2881,2894 ---- # globalize vars - this happens here to localize the circular # references created by global_vars. $self->_globalize_vars() if ($options->{global_vars}); + $self->_globalize__associate() if ($options->{__associateyes} && !$options->{global_vars}); + + #main::dump($options->{global_vars}, $options->{__associateyes}, + # scalar(@{$options->{associate}}), scalar(@{$options->{__associate}})); # support the associate magic, searching for undefined params and # attempting to fill them from the associated objects. ! if (scalar(@{$options->{associate}})) {# true when $options->{global_vars} set # prepare case-mapping hashes to do case-insensitive matching # against associated objects. This allows CGI.pm to be # case-sensitive and still work with asssociate. *************** *** 2478,2494 **** foreach my $param (keys %{$self->{param_map}}) { unless (defined($self->param($param))) { OBJ: foreach my $associated_object (reverse @{$options->{associate}}) { ! $self->param($param, scalar $associated_object->param($case_map{$associated_object}{$param})), last OBJ ! if (exists($case_map{$associated_object}{$param})); } } } } use vars qw($line @parse_stack); local(*line, *parse_stack); # walk the parse stack, accumulating output in $result ! *parse_stack = $self->{parse_stack}; my $result = ''; tie $result, 'HTML::Template::PRINTSCALAR', $args{print_to} --- 2909,3021 ---- foreach my $param (keys %{$self->{param_map}}) { unless (defined($self->param($param))) { OBJ: foreach my $associated_object (reverse @{$options->{associate}}) { ! $lparam=$param; $lparam=~s/^:://; ! if(exists($case_map{$associated_object}{$lparam})){ ! my $av=scalar $associated_object->param($case_map{$associated_object}{$lparam}); ! if(ref($av) eq 'ARRAY' && ref($self->{param_map}->{$param}) ne 'HTML::Template::LOOP'){ ! $av=@{$av}?'HTML::Template::LOOP':''; # just a dummy value ! } ! $self->param($param, $av); ! last OBJ; ! } ! } ! } ! } ! }elsif(scalar(@{$options->{__associate}})){ ! my (%case_map, $lparam); ! foreach my $associated_object (@{$options->{__associate}}) { ! if ($options->{case_sensitive}) { ! map { ! $case_map{$associated_object}{$_} = $_ ! } $associated_object->param(); ! } else { ! map { ! $case_map{$associated_object}{lc($_)} = $_ ! } $associated_object->param(); ! } ! } ! ! foreach my $param (grep /^::\w+$/, keys %{$self->{exprs}}) { ! unless (defined($self->param($param))) { ! OBJ: foreach my $associated_object (reverse @{$options->{__associate}}) { ! $lparam=$param; $lparam=~s/^:://; ! $self->param($param, scalar $associated_object->param($case_map{$associated_object}{$lparam})), last OBJ ! if (exists($case_map{$associated_object}{$lparam})); } } } } + #print STDERR 'output called ',main::tracer(2),"\n"; + #FacultyDatabase::dump('!!!!parse_stack',$self->{parse_stack}); + #FacultyDatabase::dump('!!!!!param_map',$self->{param_map}); + #FacultyDatabase::dump('!!!!!exprs',$self->{exprs},$options); + + foreach my $param (keys %{$self->{exprs}}) { # eval any extended exprs + #print STDERR 'loop ',$param,' : ',$self->{exprs}->{$param},' = ',$self->param($param),"\n"; + #loop MD55dc5a32ae024883c4e5551922d2e20da : #newres && !#prn = 1 + next if $param=~/^::\w+$/ or defined($self->param($param)) and not defined $self->{exprs}->{$param}; + #print STDERR 'loop ',$self,' : ',$param,' : ',$self->{exprs}->{$param},"\n"; + + my $lparam=$param; # save a copy + $param=$self->{exprs}->{$param} if defined $self->{exprs}->{$param}; # get the real expr + + my $fs="\x8d"; my ($p,$q,$z); # a non \w + next if defined($self->param($lparam)) and $param=~m,$fs(\w+),s; # use the saved MD5*=value, if any FUNCs used + $param=~s,#([:\w]+)$fs, $p=$1; # subs all param values first + $q=$self->param($p); + if(defined($q)){ $z=1; }else{ $q='';} # $z tracks if any params are defined. + $q=~s/\\*\'/\\\'/sg; # escape ' + $q=~/^\d+$/s? $q : "'".$q."'"; + ,seg; + + next if defined($self->param($param)) and !$z; # use the saved value + + $param=~s,$fs(\w+), # subs and check all func calls + die "HTML::Template: bad function \"$1\" in an extended expr." if !$FUNC{$1}; + $FUNC{$1}; + ,seg; + + if(exists $CACHE{$param}){# just borrow the cache + $p=$CACHE{$param}; # to prevent stale cache, it's important to sub all param values above first + }else{# now we are ready to eval it + #### DO NOT CHANGE ANYTHING UNLESS YOU KNOW WHAT YOU ARE DOING! #### + $q=$param; # a copy to save $param + #print STDERR 'eval ',$q,"\n";# ':',$lparam, + {# subs `...`. see above for others + $q=~s,\`,\x8d,sg; + } + ($q) = $q =~ /(.*)/s; ## untaint data. + #print STDERR "=e====== $q ====\n"; + $p=eval $q; #print STDERR 'eval ',$q,' = ',$p,"\n"; &FacultyDatabase::dump($p); # should we mask out s/.../.../e ? + if(!$@){# there is no error + {# subs back `...`,$,@,% + $p=~s,\x8d,\`,sg; + $p=~s,\x90,\$,sg; + $p=~s,\x9d,\@,sg; + #$p=~s,\x8f,\%,sg; + } + $CACHE{$param}=$p if not ref($p); # never cache reference + }else{ + #die "HTML::Template: Extended expression: $q\nError: $@"; + } + #FacultyDatabase::dump($self); + } + #print STDERR "###Xuse =!!!self====== $self == $HTML::Template::LOOP::VHRF ==\n"; + #FacultyDatabase::dump($HTML::Template::LOOP::VHRF,$lparam,$p,$q); + if(defined($p)){ + $self->param($lparam, $p); + + if(defined $HTML::Template::LOOP::VHRF && !exists $HTML::Template::LOOP::VHRF->{$lparam}){ + $HTML::Template::LOOP::VHRF->{$lparam}=$p; + } + } + } + use vars qw($line @parse_stack); local(*line, *parse_stack); # walk the parse stack, accumulating output in $result ! *parse_stack = $self->{parse_stack}; #main::dump($self->{parse_stack}); my $result = ''; tie $result, 'HTML::Template::PRINTSCALAR', $args{print_to} *************** *** 2505,2511 **** } elsif ($type eq 'HTML::Template::VAR' and ref($$line) eq 'CODE') { defined($$line) and $result .= $$line->($self); } elsif ($type eq 'HTML::Template::VAR') { ! defined($$line) and $result .= $$line; } elsif ($type eq 'HTML::Template::LOOP') { if (defined($line->[HTML::Template::LOOP::PARAM_SET])) { eval { $result .= $line->output($x, $options->{loop_context_vars}); }; --- 3032,3042 ---- } elsif ($type eq 'HTML::Template::VAR' and ref($$line) eq 'CODE') { defined($$line) and $result .= $$line->($self); } elsif ($type eq 'HTML::Template::VAR') { ! if(defined($$line)){ ! $result .= $$line; ! }elsif($DEF{$line}){ ! $result .= $DEF{$line}; ! } } elsif ($type eq 'HTML::Template::LOOP') { if (defined($line->[HTML::Template::LOOP::PARAM_SET])) { eval { $result .= $line->output($x, $options->{loop_context_vars}); }; *************** *** 2575,2580 **** --- 3106,3127 ---- $toencode =~ s!([^a-zA-Z0-9_.\-])!$URLESCAPE_MAP{$1}!g; $result .= $toencode; } + } elsif ($type eq 'HTML::Template::SQLESCAPE') { + $x++; + *line = \$parse_stack[$x]; + if (defined($$line)) { + my $toencode = $$line; + + # For MySQL here: escSQLChars + $toencode=~s/\\/\\\\/gs; # this one has to be the first to subs! + $toencode=~s/'/\\'/gs; + $toencode=~s/\n/\\n/gs; + $toencode=~s/\r/\\r/gs; + $toencode=~s/\0/\\0/gs; + + $result .= $toencode; + } + next; } else { confess("HTML::Template::output() : Unknown item in parse_stack : " . $type); } *************** *** 2582,2591 **** # undo the globalization circular refs $self->_unglobalize_vars() if ($options->{global_vars}); print STDERR "### HTML::Template Memory Debug ### END OUTPUT ", $self->{proc_mem}->size(), "\n" if $options->{memory_debug}; ! return undef if defined $args{print_to}; return $result; } --- 3129,3143 ---- # undo the globalization circular refs $self->_unglobalize_vars() if ($options->{global_vars}); + $self->_unglobalize__associate() if ($options->{__associateyes} && !$options->{global_vars}); print STDERR "### HTML::Template Memory Debug ### END OUTPUT ", $self->{proc_mem}->size(), "\n" if $options->{memory_debug}; ! ! undef $SELF; ! ! #&FacultyDatabase::dump('==param_map==',$self->{param_map},'==exprs==',$self->{exprs}); ! $result=~s///sg; # remove a section from output return undef if defined $args{print_to}; return $result; } *************** *** 2764,2769 **** --- 3316,3322 ---- return $self; } + use vars qw($VHRF); sub output { my $self = shift; my $index = shift; *************** *** 2771,2781 **** my $template = $self->[TEMPLATE_HASH]{$index}; my $value_sets_array = $self->[PARAM_SET]; return unless defined($value_sets_array); ! my $result = ''; my $count = 0; my $odd = 0; foreach my $value_set (@$value_sets_array) { if ($loop_context_vars) { if ($count == 0) { @{$value_set}{qw(__first__ __inner__ __last__)} = (1,0,$#{$value_sets_array} == 0); --- 3324,3338 ---- my $template = $self->[TEMPLATE_HASH]{$index}; my $value_sets_array = $self->[PARAM_SET]; return unless defined($value_sets_array); ! #FacultyDatabase::dump($value_sets_array); ! #FacultyDatabase::dump($self); ! my $result = ''; my $count = 0; my $odd = 0; + my $n_value_sets=@$value_sets_array; foreach my $value_set (@$value_sets_array) { + local $VHRF=$value_set; # pass ref to %FUNC if ($loop_context_vars) { if ($count == 0) { @{$value_set}{qw(__first__ __inner__ __last__)} = (1,0,$#{$value_sets_array} == 0); *************** *** 2785,2797 **** @{$value_set}{qw(__first__ __inner__ __last__)} = (0,1,0); } $odd = $value_set->{__odd__} = not $odd; } $template->param($value_set); ! $result .= $template->output; $template->clear_params; ! @{$value_set}{qw(__first__ __last__ __inner__ __odd__)} = (0,0,0,0) ! if ($loop_context_vars); $count++; } return $result; --- 3342,3357 ---- @{$value_set}{qw(__first__ __inner__ __last__)} = (0,1,0); } $odd = $value_set->{__odd__} = not $odd; + $value_set->{__counter__}=$count+1; + $value_set->{__count__}=$n_value_sets; } $template->param($value_set); ! $result .= $template->output; #print STDERR '==tmpl==',$template,"\n"; $template->clear_params; ! #@{$value_set}{qw(__first__ __last__ __inner__ __odd__)} = (0,0,0,0) ! # if ($loop_context_vars); $count++; + undef $VHRF; } return $result; *************** *** 2811,2823 **** package HTML::Template::NOOP; sub new { my $unused; my $self = \$unused; bless($self, $_[0]); return $self; } ! package HTML::Template::ESCAPE; sub new { my $unused; my $self = \$unused; --- 3371,3392 ---- package HTML::Template::NOOP; sub new { + my $pkg = shift; + my $var = shift; + my $self = \$var; + bless($self, $pkg); + return $self; + } + + package HTML::Template::ESCAPE; + sub new { my $unused; my $self = \$unused; bless($self, $_[0]); return $self; } ! package HTML::Template::URLESCAPE; sub new { my $unused; my $self = \$unused; *************** *** 2825,2831 **** return $self; } ! package HTML::Template::URLESCAPE; sub new { my $unused; my $self = \$unused; --- 3394,3400 ---- return $self; } ! package HTML::Template::SQLESCAPE; sub new { my $unused; my $self = \$unused; Only in .: Template.pm.bk Only in .: tags Only in ./templates/include_path: RCS Only in ./templates/searchpath: RCS diff -cr ../HTML-Template-2.5-org/test.pl ./test.pl *** ../HTML-Template-2.5-org/test.pl Mon Jan 28 23:03:46 2002 --- ./test.pl Thu Jul 25 09:43:39 2002 *************** *** 447,453 **** ); $template->param(outer_loop => [{loop => [{'LOCAL' => 'foo'}]}]); $template->param(global => 'bar'); ! $template->param(hidden_global => 'foo'); $result = $template->output(); ok($result =~ /foobar/); --- 447,453 ---- ); $template->param(outer_loop => [{loop => [{'LOCAL' => 'foo'}]}]); $template->param(global => 'bar'); ! #$template->param(hidden_global => 'foo'); $result = $template->output(); ok($result =~ /foobar/);