File Coverage

blib/lib/HTML/Merge/Compile.pm
Criterion Covered Total %
statement 19 991 1.9
branch 0 410 0.0
condition 0 46 0.0
subroutine 7 142 4.9
pod 0 134 0.0
total 26 1723 1.5


line stmt bran cond sub pod time code
1             #####################################
2             package HTML::Merge::Compile;
3             #####################################
4             BEGIN
5             {
6 1     1   1266 eval 'use HTML::Merge::Ext;';
  1     1   593  
  1         2  
  1         44  
7             }
8             # Modules ###########################
9              
10 1     1   5 use strict qw(subs vars);
  1         3  
  1         41  
11 1         97 use vars qw($open %enders %printers %tokenizers $VERSION $DEBUG
12 1     1   5 $INTERNAL_DB $INTERNAL_DB_TYPE);
  1         2  
13 1     1   6 use Carp;
  1         1  
  1         130  
14 1     1   7 use Config;
  1         3  
  1         41  
15 1     1   951 use subs qw(quotemeta);
  1         28  
  1         4  
16              
17             #####################################
18             $VERSION = '3.54';
19             #####################################
20             # Globals ###########################
21             $open = '\$R';
22             #my @non_flow = qw(VAR SQL ASSIGN SET PSET PGET PIC STATE INDEX CFG);
23             #@non_flow{@non_flow} = @non_flow;
24              
25             my @printers = qw(VERSION VAR SQL GET PGET PVAR INDEX PIC STATE CFG INI LOGIN
26             AUTH DECIDE EMPTY DATE DAY MONTH YEAR DATEDIFF LASTDAY ADDDATE
27             USER MERGE TEMPLATE TRANSFER DUMP NAME TAG COOKIE SOURCE
28             DATE2UTC UTC2DATE ENV DATEF EVAL HOUR MINUTE SECOND);
29             @printers{@printers} = @printers;
30              
31             #my @stringers = qw(IF SET PSET SETCFG);
32             #@stringers{@stringers} = @stringers;
33              
34             my @tokenizers = qw();
35             @tokenizers{@tokenizers} = @tokenizers;
36              
37             %enders = qw(END_IF IF END LOOP END_WHILE WHILE);
38              
39             $INTERNAL_DB_TYPE='SQLite';
40              
41             #####################################
42             # locate the template from the various paths
43             sub GetTemplateFromPath
44             {
45 0     0 0   my ($template) = @_;
46              
47 0           my @input = ("$HTML::Merge::Ini::TEMPLATE_PATH/$template",
48             "$HTML::Merge::Ini::MERGE_ABSOLUTE_PATH/public/template/$template");
49              
50             # let lets find the input
51 0           foreach (@input)
52             {
53 0 0         if (-f)
54             {
55 0           return $_;
56             }
57             }
58            
59 0           return "$HTML::Merge::Ini::TEMPLATE_PATH/$template";
60             }
61             #####################################
62             sub WantPrinter
63             {
64 0     0 0   my ($self, $tag, $dtag, $dline) = @_;
65              
66 0           my $ret = $self->WantTag($tag);
67 0 0         return $ret if ($printers{$tag});
68 0           my $line = $self->Line;
69 0           $self->Die("$tag is not an output tag, perhaps you forgot to close a string in tag $dtag from line $dline? Output tags are " . join(", ", keys %printers));
70             }
71             #####################################
72             sub Translate
73             {
74 0     0 0   my ($self, $exp) = @_;
75 0           my $result = "\\\\[=\\.]";
76 0           my $i;
77             my @fetch;
78 0           my $tail;
79              
80 0           while ($exp =~ s/^(.*?)([QUELD])//i)
81             {
82 0           my ($before, $token) = ($1, uc($2));
83 0           $result .= quotemeta(quotemeta($before));
84              
85 0 0         if ($token eq 'U')
    0          
    0          
    0          
    0          
86             {
87 0           $result .= '(.*?)';
88 0           $i++;
89 0           push(@fetch, "\$$i");
90             }
91             elsif ($token eq 'L')
92             {
93 0           $result .= '([A-Z])';
94 0           $i++;
95 0           push(@fetch, "\$$i");
96             }
97             elsif ($token eq 'Q')
98             {
99 0           $i++;
100 0           $result .= "\\\\(['\"])(.*?)\\\\\\$i";
101 0           $i++;
102 0           push(@fetch, "\$$i");
103             }
104             elsif ($token eq 'E')
105             {
106 0           $result .= '(?:';
107 0           $tail = ')?' . $tail;
108             }
109             elsif ($token eq 'D')
110             {
111 0           $result .= "\\\\[\\.=]";
112             }
113             else
114             {
115 0           $self->Die("Unknown notator: $token");
116             }
117             }
118              
119 0           $result .= quotemeta(quotemeta($exp)) . $tail;
120 0           my $fetch = '(' . join(", ", @fetch) . ')';
121 0           ($result, $fetch);
122             }
123             #################################
124             # CGI parsing utility #
125             #################################
126             sub ParseForm
127             {
128 0     0 0   my $toParse = shift;
129 0           my ($name , $value , @pairs , $pair , %FORM);
130 0           @pairs = split(/&/, $toParse);
131 0           foreach $pair (@pairs) {
132 0           ($name, $value) = split(/=/, $pair);
133 0           $value =~ tr/+/ /;
134 0           $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
  0            
135 0           $FORM{$name} = $value;
136             #Debug("kak : $name \= $value");
137             }
138 0           return \%FORM;
139             }
140             #####################################
141             sub CgiParse
142             {
143 0     0 0   my $GFORM = &ParseForm($ENV{'QUERY_STRING'});
144 0           my $buffer;
145 0           read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
146 0           my $PFORM = &ParseForm($buffer);
147              
148 0           my (%FORM , $key);
149 0           foreach $key(keys %$GFORM){
150 0           $FORM{$key} = $GFORM->{$key};
151             }
152              
153 0           foreach $key(keys %$PFORM){
154 0           $FORM{$key} = $PFORM->{$key};
155             }
156 0           return \%FORM;
157             }
158             #####################################
159             sub WantTag
160             {
161 0     0 0   my ($self, $tag, $inv) = @_;
162 0           my $candidate = $enders{$tag};
163 0 0 0       if ($candidate && !$inv)
164             {
165 0           $tag = $candidate;
166 0           $inv = 1;
167             }
168 0 0         my $un = $inv ? "Un" : "";
169 0           my $code = UNIVERSAL::can($self, "Do$un$tag");
170 0 0         return $code if $code;
171 0           my $macro = UNIVERSAL::can('HTML::Merge::Ext', "MACRO_$tag");
172 0 0         if ($macro)
173             {
174 0           my $proto = prototype("HTML::Merge::Ext::MACRO_$tag");
175 0           my $text = quotemeta(&$macro);
176 0 0         $proto = " ($proto)" if $proto;
177              
178 0           eval <
179             package HTML::Merge::Ext;
180              
181             sub API_$tag$proto
182             {
183             Macro("$text", \@_);
184             }
185             EOM
186             }
187              
188 0           foreach my $api (qw(API OUT))
189             {
190 0           my $candidate = "RUN${api}_$tag";
191 0           my $code = UNIVERSAL::can('HTML::Merge::Ext', $candidate);
192 0 0         if ($code)
193             {
194 0           my $proto = prototype("HTML::Merge::Ext::$candidate");
195 0           $proto =~ s/;.*$//;
196 0 0         $self->Die("Prototype for $candidate may include only \$ signs")
197             unless ($proto =~ /^\$*$/);
198 0           my $check = "${api}_$tag";
199 0           my $code = UNIVERSAL::can('HTML::Merge::Ext', $check);
200 0 0         unless ($code)
201             {
202 0           my @par;
203 0           my $i = 0;
204 0           foreach (split(//, $proto))
205             {
206 0           push(@par, qq{"\$_[$i]"});
207 0           $i++;
208             }
209 0           my $pass = join(", ", @par);
210 0           my $text = "package HTML::Merge::Ext;
211             sub $check ($proto)
212             {
213             $candidate($pass);
214             }";
215 0           eval $text;
216 0 0         die $@ if $@;
217 0           last;
218             }
219             }
220             }
221 0 0         my @options = !$inv ? qw(API OAPI OUT) : qw(CAPI);
222 0           foreach my $api (@options)
223             {
224 0           my $candidate = "${api}_$tag";
225 0           $code = UNIVERSAL::can('HTML::Merge::Ext', $candidate);
226 0 0         if ($code)
227             {
228 0           my $ref = ref($self);
229 0           my $proto = prototype("HTML::Merge::Ext::$candidate");
230 0           $proto =~ s/;.*$//;
231 0 0         $self->Die("Prototype for $candidate may include only \$ signs")
232             unless ($proto =~ /^\$*$/);
233 0           my $n = length($proto);
234 0           my $shift = join(", ",
235 0           map {"\$param[$_]";} (0 .. $n - 1));
236 0           my $stack;
237 0           my $scope = lc($tag);
238 0 0         if ($api eq 'OAPI')
239             {
240 0           $stack = qq!\$self->Push('$scope', \$engine);!;
241             }
242 0 0         if ($api eq 'CAPI')
243             {
244 0           $stack = qq!\$self->Expect(\$engine, '$scope');!
245             }
246 0           my $desc = UNIVERSAL::can('HTML::Merge::Ext',
247             "DESC_$tag");
248 0           my $expand;
249 0 0         unless ($desc)
250             {
251 0           $expand = 'my @param = @$param;';
252 0           $tokenizers{$tag} = 1;
253             }
254             else
255             {
256 0 0         if ($api eq 'CAPI')
257             {
258 0           $expand = 'my @param;';
259             }
260             else
261             {
262 0           my $txt = &$desc;
263 0           my ($re, $form) = $self->Translate($txt);
264 0           $expand = <
265             unless (\$param =~ /^$re\$/s)
266             {
267             \$self->Syntax;
268             }
269             my \@param = $form;
270             EOM
271             }
272             }
273 0           my $extend = <
274             package $ref;
275             sub Do$un$tag
276             {
277             my (\$self, \$engine, \$param) = \@_;
278             $expand
279             my \$n = \@param;
280             \$self->Die("$n parameters expected for $tag, gotten \$n") unless (\$n == $n);
281             $stack
282             \$HTML::Merge::Ext::ENGINE = \$engine;
283             \$HTML::Merge::Ext::COMPILER = \$self;
284             HTML::Merge::Ext::$candidate($shift);
285             }
286             EOM
287 0           eval $extend;
288 0 0         $self->Die($@) if $@;
289 0           $printers{$tag} = ($api eq 'OUT');
290 0           return $self->WantTag($tag, $inv);
291             }
292             }
293 0           $self->Die("$tag is not a valid Merge tag");
294             }
295             #####################################
296             sub quotemeta {
297 0     0     my $text = CORE::quotemeta(shift);
298 0           $text =~ s/\\ / /g;
299 0           $text =~ s/\\\t/\t/g;
300 0           $text;
301             }
302             #####################################
303             sub Compile {
304 0     0 0   my $self = {'buffer' => '', 'scopes' => []};
305 0           my $class = __PACKAGE__;
306 0           my $in = $HTML::Merge::config;
307 0           $in =~ s|/\w+\.\w+$||;
308 0           $in =~ s|^/*||;
309 0           $in =~ s/[\/\\]/::/g;
310 0           $in =~ tr/A-Za-z0-9_://cd;
311 0 0         if ($in) {
312 0           my $code = <
313             package ${class}::$in;
314             use strict 'vars';
315             use vars qw(\@ISA);
316             \@ISA = qw($class);
317             EOM
318 0           eval $code;
319 0 0         die $@ if $@;
320 0           $class .= "::$in";
321             }
322 0           bless $self, $class;
323 0           $self->{'source'} = shift;
324 0           $self->{'source'} =~ s/\r\n/\n/g;
325 0           $self->{'save'} = $self->{'source'};
326 0           $self->{'name'} = shift;
327 0           $self->{'template'} = $self->{'name'};
328 0           $self->{'template'} =~ s|^$HTML::Merge::Ini::TEMPLATE_PATH/||;
329 0           $self->{'force line'} = shift;
330 0           $self->Main;
331 0           $self->{'buffer'};
332             }
333             #####################################
334             sub Clone {
335 0     0 0   my $self = shift;
336              
337 0           return bless {},ref($self);
338             }
339             #####################################
340             sub Clause {
341 0     0 0   my ($self,$text,$in) = @_;
342              
343 0           my $new=$self->Clone();
344 0           my $error;
345             my $res;
346              
347 0           $new->{'save'} = $new->{'source'} = "$text>";
348 0           eval{
349 0           $res=$new->EatParam($in);
350             };
351              
352 0 0         if($@){
353 0           $error=$@;
354 0           $error=~ s/ at .* line .*$//;
355 0           $self->Die($error);
356             }
357 0           $res=~ s/\n+$//s;
358              
359 0           return $res;
360             }
361             #####################################
362             sub Line {
363 0     0 0   my $self = shift;
364 0           my $force = $self->{'force line'};
365 0 0         return $force if $force;
366 0           my @lines = split(/\n/, $self->{'save'});
367 0           my $left = substr($self->{'save'}, -length($self->{'source'}));
368 0           my @ll = split(/\n/, $left);
369 0           my $this = @lines - @ll + 1;
370             }
371             #####################################
372             sub Mark {
373 0     0 0   my $self = shift;
374 0           my $name = $self->{'name'};
375 0           my $this = $self->Line;
376 0 0         return unless $name;
377 0           $self->{'buffer'} .= "\$HTML::Merge::context = [\"$name\", \"$this\"];\n";
378 0           $self->{'buffer'} .= "#line $this $name\n";
379 0           return;
380             }
381             #####################################
382             sub Die {
383 0     0 0   my ($self, $error) = @_;
384 0           my $this = $self->Line;
385 0           my $s = (split(/\n/, $self->{'save'}))[$this - 1];
386 0           my $name = $self->{'name'};
387 0 0         if ($error < 0) {
388 0           die "Depcrecated: Die(negative)";
389             }
390              
391 0           $name =~ s|^.*/||;
392 0 0 0       Carp::cluck "Error: $error at $name line $this when doing: $s" if $DEBUG
393             || $ENV{'MERGE_DEBUG'};
394 0           die "Error: $error at $name line $this, when doing: $s";
395             }
396             #####################################
397             sub Main {
398 0     0 0   my $self = shift;
399 0           $self->{'source'} =~ s/<(BODY)/\n<$1/i;
400 0           while ($self->EatOne) {}
401 0           $self->PrePrint($self->{'source'});
402 0           $self->{'source'} = '';
403 0 0         if (@{$self->{'scopes'}}) {
  0            
404 0           my @scopes = map {join("/", @$_);} @{$self->{'scopes'}};
  0            
  0            
405 0           my $stack = join(", ", @scopes);
406 0           $self->Die("Stack not empty: $stack");
407             }
408             }
409             #####################################
410             sub EatOne {
411 0     0 0   my $self = shift;
412 0 0         if ($self->{'source'} =~ s/^(.*?)\<(\/?)$open(\[.+?\]\.)?(\w+)//si) {
413 0           my ($head, $close, $engine, $tag, $param) = ($1, $2, $3, uc($4));
414 0           $engine =~ s/^\[(.*)\]\./$1/;
415 0 0         $engine= $self->Clause($engine,$tag) if($engine=~ /\<$open/);
416            
417 0           my $code = $self->WantTag($tag, $close);
418 0           $param = $self->EatParam($tag);
419 0 0 0       $self->Die("Closing tags may not have parameters") if (($close || $enders{$tag}) && ($param && !ref($param) || ref($param) && $#$param >= 0));
      0        
      0        
420 0           $self->Mark;
421 0 0         if ($printers{$tag}) {
422 0           $self->PrePrint($head);
423 0           $self->{'buffer'} .= "print (";
424             } else {
425 0           $head =~ s/\s+$//s;
426 0           $self->PrePrint($head);
427             }
428 0           $self->{'buffer'} .= &$code($self, $engine, $param);
429 0 0         if ($printers{$tag}) {
430 0           $self->{'buffer'} .= ");\n";
431             }
432 0           return 1;
433             }
434 0           undef;
435             }
436             #####################################
437             sub Macro {
438 0     0 0   my ($self, $text) = @_;
439 0           my $length = length($self->{'source'});
440 0           my $lennow;
441              
442 0           $self->{'source'} = $text . $self->{'source'};
443 0           for (;;) {
444 0           $lennow = length($self->{'source'});
445 0 0         last if ($lennow <= $length);
446 0           my $left = $lennow - $length;
447 0 0         last if $self->{'source'} =~ /^\s{$left}/;
448              
449 0 0         $self->EatOne || last;
450             }
451 0           my $remainder = $lennow - $length;
452 0 0         $self->Die("macro did not resolve correctly") if ($remainder < 0);
453 0           $self->PrePrint(substr($self->{'source'}, 0, $remainder));
454 0           substr($self->{'source'}, 0, $remainder) = "";
455             }
456             #####################################
457             sub PrePrint {
458 0     0 0   my ($self, $string) = @_;
459 0           while ($string =~ s/^(.*?)\0(.*?)\0//) {
460 0           my ($b4, $bt) = ($1, $2);
461              
462 0           $self->Print($b4);
463 0           $self->{'buffer'} .= qq'print "$bt";';
464             }
465 0 0         $self->Print($string) if $string;
466             }
467             #####################################
468             sub Print {
469 0     0 0   my ($self, $string) = @_;
470 0           my @lines = split(/\n/, $string);
471 0           my $last = pop @lines;
472 0           foreach (@lines) {
473 0           $self->{'buffer'} .= 'print "' . quotemeta($_) . '\n";' . "\n";
474             }
475 0           $self->{'buffer'} .= 'print "' . quotemeta($last) . '";' . "\n";
476 0 0         $self->{'buffer'} .= 'print "\n";' . "\n" if ($string =~ /\n$/);
477             }
478             #####################################
479             sub EatParam {
480 0     0 0   my ($self, $in) = @_;
481 0           my $tokens = $tokenizers{$in};
482 0           my $line = $self->Line;
483 0           my $state = '';
484 0           my $text = '';
485 0           my @tokens;
486 0           for (;;) {
487 0           my $ch;
488 0 0         if ($self->{'source'} =~ s/^(.)//s) {
489 0           $ch = $1;
490             } else {
491 0           $self->Die("Could not close tag $in, probably unbalanced quotes");
492             }
493 0 0         if ($ch eq "\0") {
494 0 0         unless ($self->{'source'} =~ s/^(.*?)\0//) {
495 0           $self->Die("Unclosed null encpasulation. Check your macro");
496             }
497 0           $text .= $1;
498 0           next;
499             }
500 0 0 0       if ($ch eq "'" && $state ne '"') {
501 0           $text .= "\\'";
502 0 0         $state = ($state eq "'" ? '' : "'");
503 0           next;
504             }
505 0 0 0       if ($ch eq '"' && $state ne "'") {
506 0           $text .= "\\\"";
507 0 0         $state = ($state eq '"' ? '' : '"'); #'"
508 0           next;
509             }
510 0 0         if ($ch eq "\\") {
511 0           $self->{'source'} =~ s/^(.)//s;
512 0           $ch = $1;
513 0           $text .= "\\$ch";
514 0           next;
515             }
516 0 0 0       if ($ch eq '>' && !$state) {
517 0           $text =~ s/\s+$//;
518 0 0         return $text unless $tokens;
519 0 0         return [] unless @tokens;
520 0           my $pre = shift @tokens;
521 0 0         $self->Die("Illegal prefix $pre") if $pre;
522 0           push(@tokens, $text);
523 0           return \@tokens;
524             }
525 0 0 0       if ($ch eq '.' && !$state && $tokens) {
      0        
526 0           push(@tokens, $text);
527 0           $text = '';
528 0           next;
529             }
530 0 0         if ($ch eq "<") {
531 0 0         unless ($self->{'source'} =~ s/^$open//) {
532 0           $text .= "<";
533 0 0         $text .= $self->FindRight if $in eq 'EM';
534 0           next;
535             }
536 0           $self->{'source'} =~ s/(\[.+?\]\.)?(\w+)//;
537 0           my $engine = $1;
538 0           my $tag = uc($2);
539 0           $engine =~ s/^\[(.*)\]\./$1/;
540 0 0         $engine= $self->Clause($engine,$tag) if($engine=~ /\<$open/);
541 0           my $code;
542 0 0         if ($in ne 'EM') {
543 0           $code = $self->WantPrinter($tag, $in, $line);
544             }
545 0 0         my $sub = $self->EatParam($in eq 'EM' ? 'EM' : $tag);
546 0 0         if ($in ne 'EM') {
547 0           $text .= '" . (' . &$code($self, $engine, $sub) . ') . "';
548             }
549             } else {
550 0           $text .= quotemeta($ch);
551             }
552             }
553             }
554             #####################################
555             sub FindRight {
556 0     0 0   my $self = shift;
557 0           my $count = 1;
558 0           my $text;
559 0           while ($self->{'source'} =~ s/^(.*?)([\<\>])//) {
560 0           $text .= "$1$2";
561 0 0         $count += $2 eq '<' ? 1 : -1;
562 0 0         return $text unless $count;
563             }
564 0           return $text;
565             }
566             #####################################
567             sub Expect {
568 0     0 0   my ($self, $engine, @options) = @_;
569 0           my $current = pop @{$self->{'scopes'}};
  0            
570 0           my @topt = @options;
571 0           my $last = pop @topt;
572 0 0         my $expect = join(", ", @topt) . (@topt ? ' or ' : '') . $last;
573 0 0         $self->Die("Stack underflow - a closing tag without a preceding tag, expecting: $expect. Perhaps you forgot $open in the opening tag?") unless ($current);
574 0           my ($scope, $teng) = @$current;
575 0 0         $self->Die("Expected engine '$engine', got '$teng'") unless ($teng eq $engine);
576 0           foreach (@options) {
577 0 0         return if ($_ eq $scope);
578             }
579 0           $self->Die("Unexpected scope $scope, expecting: $expect. Perhaps you forgot $open in the opening tag?");
580             }
581             #####################################
582             sub Push {
583 0     0 0   my ($self, $scope, $engine) = @_;
584 0           push(@{$self->{'scopes'}}, [$scope, $engine]);
  0            
585             }
586             #####################################
587             sub DoLOOP {
588 0     0 0   my ($self, $engine, $param) = @_;
589 0           my $limit = undef;
590 0 0         if ($param =~ s/^\\\.LIMIT\\=((?:\\['"])?)(.+)\1$//s) { #'
591 0           $limit = $2;
592             }
593 0 0         $self->Syntax if $param;
594 0           my $text;
595 0 0         unless ($limit) {
596 0           $text = <
597             local (\$_);
598             for (;;) {
599             \$_++;
600             EOM
601             } else {
602 0           $text = <
603             HTML::Merge::Engine::Force("$limit", 'iu');
604             foreach (1 .. "$limit") {
605             EOM
606             }
607 0           $text .= <
608             last unless (\$engines{"$engine"}->HasQuery);
609             last unless (\$engines{"$engine"}->Fetch(1, \$_));
610             local (\$_);
611             EOM
612 0           $self->Push('loop', $engine);
613 0           $text;
614             }
615             #####################################
616              
617             *DoEPEAT = \&DoITERATION;
618             *DoUnEPEAT = \&DoUnITERATION;
619              
620             #####################################
621             sub DoITERATION {
622 0     0 0   my ($self, $engine, $param) = @_;
623 0 0         unless ($param =~ /^\\\.LIMIT\\=((?:\\['"])?)(.+)\1$/s) { #'
624 0           $self->Syntax;
625             }
626 0           my $limit = $2;
627 0           $self->Push('iteration', $engine);
628 0           <
629             HTML::Merge::Engine::Force("$limit", 'ui');
630             foreach (1 .. "$limit") {
631             EOM
632             }
633             #####################################
634             sub DoUnITERATION {
635 0     0 0   my ($self, $engine, $param) = @_;
636 0           $self->Expect($engine, 'iteration');
637 0           "}\n";
638             }
639             #####################################
640             sub DoBREAK {
641 0     0 0   my ($self, $engine, $param) = @_;
642 0 0         $self->Syntax if ($param);
643 0           "last;";
644             }
645              
646             #####################################
647             sub DoCONT {
648 0     0 0   my ($self, $engine, $param) = @_;
649 0 0         $self->Syntax if ($param);
650 0           "next;";
651             }
652             #####################################
653             sub DoUnLOOP {
654 0     0 0   my ($self, $engine, $param) = @_;
655 0           $self->Expect($engine, 'loop');
656 0           "}\n";
657             }
658             #####################################
659             sub DoFETCH {
660 0     0 0   my ($self, $engine, $param) = @_;
661 0 0         $self->Syntax if ($param);
662 0           "\$engines{\"$engine\"}->Fetch(1, 2);";
663             }
664             #####################################
665              
666             *DoENVGET = \&DoENV;
667              
668             #####################################
669             sub DoENV {
670 0     0 0   my ($self, $engine, $param) = @_;
671 0 0         unless ($param =~ s/^\\\.(.+)$//s) {
672 0           $self->Syntax;
673             }
674 0           return "\$ENV{\"$1\"}";
675             }
676             #####################################
677             sub DoENVSET {
678 0     0 0   my ($self, $engine, $param) = @_;
679 0 0         unless ($param =~ s/^\\\.(.+?)\\=\\(['"])(.*?)\\\2$//s) {
680 0           $self->Syntax;
681             }
682 0           "\$ENV{\"$1\"} = eval(\"$3\");\n";
683             }
684             #####################################
685             sub DoCFG {
686 0     0 0   my ($self, $engine, $param) = @_;
687 0 0         unless ($param =~ s/^\\\.(.+)$//s) {
688 0           $self->Syntax;
689             }
690 0           "\${\"HTML::Merge::Ini::\" . \"$1\"}";
691             }
692             #####################################
693              
694             *DoINIGET = *DoINI = *DoCFGGET = \&DoCFG;
695             *DoINISET = \&DoCFGSET;
696              
697             #####################################
698             sub DoCFGSET {
699 0     0 0   my ($self, $engine, $param) = @_;
700 0 0         unless ($param =~ s/^\\\.(.+?)\\=\\(['"])(.*)\\\2$//s) {
701 0           $self->Syntax;
702             }
703 0           "\${\"HTML::Merge::Ini::\" . \"$1\"} = eval(\"$3\");\n";
704             }
705             #####################################
706              
707             *DoVAL = \&DoVAR;
708              
709             #####################################
710             sub DoVAR
711             {
712 0     0 0   my ($self, $engine, $param) = @_;
713              
714 0 0         unless ($param =~ s/^\\\.(.+)$//s)
715             {
716 0           $self->Syntax;
717             }
718              
719 0           return "\$vars{\"$1\"}";
720             }
721             #####################################
722             sub DoVERSION
723             {
724 0     0 0   my ($self, $engine, $param) = @_;
725              
726 0           return $VERSION;
727             }
728             #####################################
729             sub DoSQL
730             {
731 0     0 0   my ($self, $engine, $param) = @_;
732              
733 0 0         unless ($param =~ s/^\\\.(.+)$//s)
734             {
735 0           $self->Syntax;
736             }
737              
738 0           return "\$engines{\"$engine\"}->Var(\"$1\")";
739             }
740             #####################################
741             sub DoIF
742             {
743 0     0 0   my ($self, $engine, $param) = @_;
744              
745 0 0         unless ($param =~ s/^\\[\.=]\\(['"])(.*)\\\1$//s)
746             {
747 0           $self->Syntax;
748             }
749              
750 0           my $text = <
751             HTML::Merge::Error::HandleError('INFO', "$2", 'IF');
752             my \$__test = eval("$2");
753             HTML::Merge::Error::HandleError('ERROR', \$@) if (\$@);
754             if (\$__test) {
755             EOM
756 0           $self->Push('if', $engine);
757 0           $text;
758             }
759             #####################################
760             sub DoTIF
761             {
762 0     0 0   my ($self, $engine, $param) = @_;
763 0 0         unless ($param =~ s/^\\[\.=]\\(['"])(.*)\\\1$//s)
764             {
765 0           $self->Syntax;
766             }
767              
768 0           my $text = <
769             HTML::Merge::Error::HandleError('INFO', "$2", 'IF');
770             my \$__test = "$2";
771             HTML::Merge::Error::HandleError('ERROR', \$@) if (\$@);
772             if ("$2") {
773             EOM
774 0           $self->Push('if', $engine);
775 0           $text;
776             }
777             #####################################
778             sub DoUnTIF {
779 0     0 0   my ($self, $engine, $param) = @_;
780 0           $self->Expect($engine, 'if', 'else');
781 0           "}\n";
782             }
783             #####################################
784             sub DoELSIF {
785 0     0 0   my ($self, $engine, $param) = @_;
786 0 0         unless ($param =~ s/^\\[\.=]\\(['"])(.*)\\\1$//s) {
787 0           $self->Syntax;
788             }
789 0           $self->Expect($engine, 'if');
790 0           $self->Push('if', $engine);
791 0           my $text = <
792             \$__exit = 0;
793             } elsif (((HTML::Merge::Error::HandleError('INFO', "$2", 'IF'),
794             \$__exit = eval("$2"),
795             \$@ && HTML::Merge::Error::HandleError('ERROR', \$@),
796             \$__exit))[-1]) {
797             EOM
798 0           $text;
799             }
800              
801              
802             sub DoUnIF {
803 0     0 0   my ($self, $engine, $param) = @_;
804 0           $self->Expect($engine, 'if', 'else');
805 0           "}\n";
806             }
807              
808             sub DoELSE {
809 0     0 0   my ($self, $engine, $param) = @_;
810 0 0         $self->Syntax if $param;
811 0           $self->Expect($engine, 'if');
812 0           $self->Push('else', $engine);
813 0           "} else {\n";
814             }
815              
816             sub DoWHILE {
817 0     0 0   my ($self, $engine, $param) = @_;
818 0 0         unless ($param =~ s/^\\[\.=]\\(['"])(.*)\\\1$//s) {
819 0           $self->Syntax;
820             }
821 0           my $cond = quotemeta($2);
822 0           my $text = <
823             HTML::Merge::Error::HandleError('INFO', "while $2", 'WHILE');
824             for (;;) {
825             my \$__test = eval("$2");
826             HTML::Merge::Error::HandleError('ERROR', \$@) if (\$@);
827             last unless \$__test;
828             EOM
829 0           $self->Push('while', $engine);
830 0           $text;
831             }
832              
833             sub DoUnWHILE {
834 0     0 0   my ($self, $engine, $param) = @_;
835 0           $self->Expect($engine, 'while');
836 0           "}\n";
837             }
838              
839             sub DoQ {
840 0     0 0   my ($self, $engine, $param) = @_;
841 0 0         unless ($param =~ s/^\\[=\.]\\(['"])(.*)\\\1$//s) {
842 0           $self->Syntax;
843             }
844 0           "\$engines{\"$engine\"}->Query(\"$2\");\n";
845             }
846              
847             sub DoS {
848 0     0 0   my ($self, $engine, $param) = @_;
849 0 0         unless ($param =~ s/^\\[\.=]\\(['"])(.*)\\\1$//s) {
850 0           $self->Syntax;
851             }
852 0           "\$engines{\"$engine\"}->Statement(\"$2\");\n";
853             }
854              
855             sub DoEVAL {
856 0     0 0   my ($self, $engine, $param) = @_;
857 0 0         unless ($param =~ s/^\\[\.=]\\(['"])(.*)\\\1$//s) {
858 0           $self->Syntax;
859             }
860 0           "eval(\"$2\")";
861             }
862             #####################################
863             sub DoPERL {
864 0     0 0   my ($self, $engine, $param) = @_;
865 0           my $type;
866 0 0         if ($param =~ s/^\\\.([ABC])$//i) {
867 0           $type = uc($1);
868             }
869 0 0         $self->Syntax if $param;
870 0           my $code = "";
871 0           my $line = $self->Line;
872 0 0 0       if ($type eq 'B' || $type eq 'C') {
873 0           my $flag;
874 0           while ($self->{'source'} =~ s/^(.*?)\<($open(?:\[.+?\]\.)?\w+|\/${open}PERL\>)//is) {
875 0           my $let = quotemeta($1);
876 0           $code .= qq!"$let" . !;
877 0           my $tag = $2;
878 0 0         if ($tag =~ m|^/${open}PERL>$|) {
879 0           $flag = 1;
880 0           last;
881             }
882 0           $tag =~ s/^$open//;
883 0           my $engine = '';
884 0 0         if ($tag =~ s/^\[(.+?)\]\.//) {
885 0           $engine = $1;
886 0 0         $engine= $self->Clause($engine,$tag) if($engine=~ /\<$open/);
887             }
888 0           my $coder = $self->WantPrinter($tag, "PERL", $line);
889 0           my $param = $self->EatParam($tag);
890 0           my $codet = &$coder($self, $engine, $param);
891 0           $code .= "$codet . ";
892             }
893 0 0         $self->Die("End of PERL not found") unless $flag;
894 0           $code .= q!""!;
895             } else {
896 0 0         unless ($self->{'source'} =~ s/^(.*?)\<\/${open}PERL\>//is) {
897 0           $self->Die("End of PERL not found");
898             }
899 0           $code = '"' . quotemeta($1) . '"';
900             }
901 0           my $name = $self->{'name'};
902 0           my $text = <
903             \$__result = $code;
904             HTML::Merge::Error::HandleError('INFO', \$__result, 'PERL');
905             \$__result = eval("\$__result; undef;");
906             HTML::Merge::Error::HandleError('ERROR', \$@) if \$@;
907             EOM
908 0 0 0       if ($type eq 'A' || $type eq 'C') {
909 0           $line = $self->Line;
910 0           $text .= <
911             if (\$__result) {
912             use HTML::Merge::Compile;
913             eval { \$__result = &HTML::Merge::Compile::Compile(\$__result, "$name", $line); };
914             HTML::Merge::Error::HandleError('ERROR', \$@) if \$@;
915             \$__result = eval(\$__result);
916             HTML::Merge::Error::HandleError('ERROR', \$@) if \$@;
917             }
918             EOM
919             }
920 0           $text;
921             }
922             ###############################################################
923             sub DoSET
924             {
925 0     0 0   my ($self, $engine, $param) = @_;
926              
927 0 0         unless ($param =~ s/^\\\.(.+?)\\=\\(['"])(.*?)\\\2$//s)
928             {
929 0           $self->Syntax;
930             }
931              
932 0           return "\$vars{\"$1\"} = eval(\"$3\");\n";
933             }
934             ###############################################################
935             sub DoASSIGN
936             {
937 0     0 0   my ($self, $engine, $param) = @_;
938              
939 0 0         unless ($param =~ s/^\\\.(.+?)\\=\\(['"])(.*?)\\\2$//s)
940             {
941 0           $self->Syntax;
942             }
943              
944 0           return "\$vars{\"$1\"} = \"$3\";\n";
945             }
946             ###############################################################
947              
948             sub DoPCLEAR {
949 0     0 0   my ($self, $engine, $param) = @_;
950 0 0         $self->Syntax if $param;
951 0           "\$engines{\"$engine\"}->ErasePersistent;\n";
952             }
953              
954             sub DoPSET {
955 0     0 0   my ($self, $engine, $param) = @_;
956 0 0         unless ($param =~ s/^\\\.(.+?)\\=\\(['"])(.*?)\\\2$//s) {
957 0           $self->Syntax;
958             }
959 0           "\$engines{\"$engine\"}->SetPersistent(\"$1\", eval(\"$3\"));\n";
960             }
961              
962             sub DoPGET {
963 0     0 0   my ($self, $engine, $param) = @_;
964 0 0         unless ($param =~ s/^\\\.(.+)$//s) {
965 0           $self->Syntax;
966             }
967 0           return "\$engines{\"$engine\"}->GetPersistent(\"$1\")";
968             }
969              
970             *DoPVAR = \&DoPGET;
971             *DoGET = \&DoVAR;
972              
973             sub DoPIMPORT {
974 0     0 0   my ($self, $engine, $param) = @_;
975 0 0         unless ($param =~ s/^\\\.(.+)$//s) {
976 0           $self->Syntax;
977             }
978 0           return "\$hash{\"$1\"} = \$engines{\"$engine\"}->GetPersistent(\"$1\");";
979             }
980              
981             sub DoPEXPORT {
982 0     0 0   my ($self, $engine, $param) = @_;
983 0 0         unless ($param =~ s/^\\\.(.+)$//s) {
984 0           $self->Syntax;
985             }
986 0           return "\$engines{\"$engine\"}->SetPersistent(\"$1\", \$hash{\"$1\"});";
987             }
988              
989              
990             *DoREM = \&DoEM;
991 0     0 0   sub DoEM {}
992              
993             sub DoTRACE {
994 0     0 0   my ($self, $engine, $param) = @_;
995 0 0         unless ($param =~ s/^\\\.\\(['"])(.*)\\\1$//s) {
996 0           $self->Syntax;
997             }
998 0           my $line = $2;
999 0           <
1000             HTML::Merge::Error::HandleError('INFO', "$line", 'TRACE');
1001             EOM
1002             }
1003             sub DoDIE {
1004 0     0 0   my ($self, $engine, $param) = @_;
1005 0 0         unless ($param =~ s/^\\\.\\(['"])(.*)\\\1$//s) {
1006 0           $self->Syntax;
1007             }
1008 0           my $line = $2;
1009 0           <
1010             HTML::Merge::Error::HandleError('ERROR', "$line");
1011             EOM
1012             }
1013             #################################################
1014             sub DoINCLUDE
1015             {
1016 0     0 0   my ($self, $engine, $param) = @_;
1017 0           my $inc;
1018 0           my $name = $self->{'name'};
1019 0           my $text;
1020              
1021 0 0         unless ($param =~ s/^\\\.\\(['"])(.*)\\\1$//s)
1022             {
1023 0           $self->Syntax;
1024             }
1025 0           $inc = $2;
1026 0           $inc =~ s/\\(.)/$1/g;
1027              
1028             ##################################################################
1029             # require Cwd;
1030             # my $curr = &Cwd::cwd;
1031             # my @tokens = split(/\//, $self->{'name'});
1032             # pop @tokens;
1033             # my $dir = join("/", @tokens);
1034             # chdir $dir if $dir;
1035             # open(I, $inc) || $self->Die("Can't open $inc at $dir");
1036             # my $text = join("", );
1037             # close(I);
1038             # chdir $curr;
1039             # $self->{'source'} = $text . $self->{'source'};
1040             ##################################################################
1041              
1042 0           $text = <
1043             my \$__input = HTML::Merge::Compile::GetTemplateFromPath("$inc");
1044             my \$__script = "\$HTML::Merge::Ini::CACHE_PATH/$inc.pli";
1045             my \$__candidate = "\$HTML::Merge::Ini::PRECOMPILED_PATH/$inc.pli";
1046              
1047             unless (-e \$__candidate)
1048             {
1049             #HTML::Merge::Error::DoWarn('NO_TEMPLATE','$inc') unless -e \$__input;
1050             HTML::Merge::Error::HandleError('ERROR',
1051             "No template '$inc' found") unless -e \$__input;
1052              
1053             my \$__source = (stat(\$__input))[9];
1054             my \$__output = (stat(\$__script))[9];
1055             if (\$__source > \$__output) {
1056             require HTML::Merge::Compile;
1057             HTML::Merge::Compile::safecreate(\$__script)
1058             unless -e \$__script;
1059             eval ' HTML::Merge::Compile::CompileFile(\$__input, \$__script, 1); ';
1060              
1061             if(\$@)
1062             {
1063             # erase the pli file
1064             unlink(\$__script);
1065             HTML::Merge::Error::HandleError('ERROR', \$@);
1066             }
1067             }
1068             } else {
1069             \$__script = \$__candidate;
1070             }
1071             HTML::Merge::Error::HandleError('INFO',"$inc",'INCLUDE');
1072             do \$__script;
1073             HTML::Merge::Error::HandleError('ERROR', \$@) if \$@;
1074             EOM
1075 0           $text;
1076             }
1077             #################################################
1078             sub DoWEBINCLUDE {
1079 0     0 0   my ($self, $engine, $param) = @_;
1080 0 0         unless ($param =~ s/^\\\.\\(['"])(.*)\\\1$//s) {
1081 0           $self->Syntax;
1082             }
1083 0           my $url = $2;
1084 0           <
1085             if (\$HTML::Merge::Ini::WEB) {
1086             require LWP;
1087             require HTTP::Request::Common;
1088             import HTTP::Request::Common;
1089              
1090             my \$__url = "$url";
1091             \$__url = "http://\$ENV{'SERVER_NAME'}:\$ENV{'SERVER_PORT'}\$__url"
1092             unless (\$__url =~ m|://|);
1093             my \$__ua = new LWP::UserAgent;
1094             my \$__req = GET("$url");
1095             my \$__resp = \$__ua->request(\$__req);
1096             if (\$__resp->is_success) {
1097             print \$__resp->content;
1098             } else {
1099             HTML::Merge::Error::HandleError('ERROR', "Web GET to URL $url returned code " . \$__resp->code);
1100             }
1101             }
1102             EOM
1103             }
1104              
1105             sub DoINDEX {
1106 0     0 0   my ($self, $engine, $param) = @_;
1107 0 0         $self->Syntax if $param;
1108 0           "\$engines{\"$engine\"}->Index";
1109             }
1110              
1111             *DoRERUN = \&DoERUN;
1112              
1113             sub DoERUN {
1114 0     0 0   my ($self, $engine, $param) = @_;
1115 0 0         $self->Syntax if $param;
1116 0           "\$engines{\"$engine\"}->ReRun;";
1117             }
1118              
1119             *EQUEST = \&ENUMREQ;
1120              
1121             sub DoENUMREQ {
1122 0     0 0   my ($self, $engine, $param) = @_;
1123 0 0         $self->Syntax unless ($param =~ /^\\\.(.+?)\\\=(.+)$/s);
1124 0           my ($iterator, $getter) = ($1, $2);
1125 0           $self->Push('enumreq', $engine);
1126 0           qq!foreach (param()) {
1127             next if (\$_ eq "template");
1128             \$vars{"$iterator"} = \$_;
1129             \$vars{"$getter"} = \$vars{\$_};\n!;
1130             }
1131              
1132             sub DoUnENUMREQ {
1133 0     0 0   my ($self, $engine, $param) = @_;
1134 0           $self->Expect($engine, 'enumreq');
1135 0           "}\n";
1136             }
1137              
1138             sub DoENUMQUERY {
1139 0     0 0   my ($self, $engine, $param) = @_;
1140 0 0         $self->Syntax unless ($param =~ /^\\\.(.+?)\\\=(.+)$/s);
1141 0           my ($iterator, $getter) = ($1, $2);
1142 0           $self->Push('enumquery', $engine);
1143 0           qq!foreach (\$engines{"$engine"}->Columns) {
1144             \$vars{"$iterator"} = \$_;
1145             \$vars{"$getter"} = \$engines{"$engine"}->Var(\$_);\n!;
1146             }
1147              
1148             sub DoUnENUMQUERY {
1149 0     0 0   my ($self, $engine, $param) = @_;
1150 0           $self->Expect($engine, 'enumquery');
1151 0           "}\n";
1152             }
1153              
1154             sub DoMULTI {
1155 0     0 0   my ($self, $engine, $param) = @_;
1156 0 0         $self->Syntax unless ($param =~ /^\\\.(.+?)\\\=(.+)$/s);
1157 0           my ($iterator, $getter) = ($1, $2);
1158 0           $self->Push('multi', $engine);
1159 0           qq!foreach (param("$getter")) {
1160             \$vars{"$iterator"} = \$_;!;
1161             }
1162              
1163             sub DoUnMULTI {
1164 0     0 0   my ($self, $engine, $param) = @_;
1165 0           $self->Expect($engine, 'multi');
1166 0           "}\n";
1167             }
1168              
1169             sub DoGLOB {
1170 0     0 0   my ($self, $engine, $param) = @_;
1171 0 0         unless ($param =~ /^\\\.([DF])\\\.(.+?)\\=\\(['"])(.*)\\\3$/is) {
1172 0           $self->Syntax;
1173             }
1174 0           my ($how, $iterator, $mask) = (uc($1), $2, $4);
1175 0           $self->Push('glob', $engine);
1176 0 0         my $cond = $how eq 'D' ? 'unless' : 'if';
1177 0           qq!\$__x = "$mask";
1178             \$__x .= "/*" if (-d \$__x);
1179             foreach (glob(\$__x)) {
1180             next $cond -d \$_;
1181             s|^.*/||;
1182             \$vars{"$iterator"} = \$_;\n!
1183             }
1184              
1185             sub DoUnGLOB {
1186 0     0 0   my ($self, $engine, $param) = @_;
1187 0           $self->Expect($engine, 'glob');
1188 0           "}\n";
1189             }
1190              
1191             sub DoFTS {
1192 0     0 0   my ($self, $engine, $param) = @_;
1193 0 0         unless ($param =~ /^\\\.(.+?)\\=\\(['"])(.*)\\\2$/is) {
1194 0           $self->Syntax;
1195             }
1196 0           my ($iterator, $base) = ($1, $3);
1197 0           $self->Push('fts', $engine);
1198 0           <
1199             use File::Find;
1200             \@__files = ();
1201             find(sub {push(\@__files, \$File::Find::name)}, "$base");
1202             foreach (\@__files) {
1203             \$vars{"$iterator"} = \$_;
1204             EOM
1205             }
1206              
1207             sub DoUnFTS {
1208 0     0 0   my ($self, $engine, $param) = @_;
1209 0           $self->Expect($engine, 'fts');
1210 0           "}\n";
1211             }
1212              
1213             sub DoCOUNT {
1214 0     0 0   my ($self, $engine, $param) = @_;
1215 0 0         $self->Syntax unless ($param =~ /^\\\.(.+?)\\\=(.*?)\\\:(.*?)(\\,.*)?$/s);
1216 0           my ($var, $from, $to, $step) = ($1, $2, $3, $4);
1217 0   0       $step ||= "\\,1";
1218 0           $step =~ s/^\\,//;
1219              
1220 0           my $i = "\$vars{\"$var\"}";
1221 0           $self->Push('count', $engine);
1222 0           <
1223             HTML::Merge::Engine::Force("$from", "n");
1224             HTML::Merge::Engine::Force("$to", "n");
1225             HTML::Merge::Engine::Force("$step", "n");
1226             for ($i = "$from"; $i <= "$to"; $i += "$step") {
1227             EOM
1228             }
1229              
1230             sub DoUnCOUNT {
1231 0     0 0   my ($self, $engine, $param) = @_;
1232 0           $self->Expect($engine, 'count');
1233 0           "}\n";
1234             }
1235              
1236             sub DoPIC {
1237 0     0 0   my ($self, $engine, $param) = @_;
1238 0           my $type;
1239 0 0         unless ($param =~ s/^\\\.([CFRNADX])(.*)$//is) {
1240 0           $self->Syntax;
1241             }
1242 0           ($type, $param) = (uc($1), $2);
1243 0           my $code = &UNIVERSAL::can($self, "Picture$type");
1244 0           &$code($self, $param);
1245             }
1246              
1247             sub PictureF {
1248 0     0 0   my ($self, $param) = @_;
1249 0           $param =~ s/^\\\((\\?.)\\\)\\\.\\(['"])(.*?)\\\2$/$1\\$2$3\\$2/s;
1250 0 0         unless ($param =~ /^(\\?.)\\(['"])(.*?)\\\2$/s) {
1251 0           $self->Syntax;
1252             }
1253 0           my ($ch, $text) = ($1, $3);
1254 0           <
1255             "" . (\$__s = "$text", \$__s =~ s/\\s/$ch/g, \$__s)[-1]
1256             EOM
1257             }
1258              
1259             sub PictureC {
1260 0     0 0   my ($self, $param) = @_;
1261 0           my @ary;
1262             my $flag;
1263 0           $param =~ s/^\\\((.*)\\\)\\\.\\(['"])(.*?)\\\2$/$1\\.\\$2$3\\$2/s;
1264 0           while ($param =~
1265             s/^\s*\\(['"])(.*?)\\\1\s*\\=\s*\\(['"])(.*?)\\\3\s*//s) {
1266 0           push(@ary, [$2, $4]);
1267 0 0         if ($param =~ s/^\\\.//) {
1268 0           $flag = 1;
1269 0           last;
1270             }
1271 0 0         unless ($param =~ s/^\\,//) {
1272 0           $self->Syntax;
1273             }
1274             }
1275 0 0         $self->Die("Syntax error in PIC.C") unless ($flag);
1276 0 0         unless ($param =~ s/^\\(["'])(.*?)\\\1$//s) {
1277 0           $self->Syntax;
1278             }
1279 0           my $text = $2;
1280 0           my $code = <
1281             "" . (\$__s = "$text",
1282             EOM
1283 0           foreach (@ary) {
1284 0           my ($from, $to) = @$_;
1285 0           $code .= <
1286             \$__s =~ s/^$from\$/$to/g,
1287             EOM
1288             }
1289 0           $code . ", \$__s)[-1]";
1290             }
1291              
1292             sub PictureR {
1293 0     0 0   my ($self, $param) = @_;
1294 0           my @ary;
1295             my $flag;
1296 0           $param =~ s/^\\\((.*)\\\)\\\.\\(['"])(.*?)\\\2$/$1\\.\\$2$3\\$2/s;
1297 0           while ($param =~
1298             s/^\s*\\(['"])(.*?)\\\1\s*\\=\s*\\(['"])(.*?)\\\3\s*//s) {
1299 0           push(@ary, [$2, $4]);
1300 0 0         if ($param =~ s/^\\\.//) {
1301 0           $flag = 1;
1302 0           last;
1303             }
1304 0 0         unless ($param =~ s/^\\,//) {
1305 0           $self->Syntax;
1306             }
1307             }
1308 0 0         $self->Die("Syntax error in PIC.R") unless ($flag);
1309 0 0         unless ($param =~ s/^\\(["'])(.*?)\\\1$//s) {
1310 0           $self->Syntax;
1311             }
1312 0           my $text = $2;
1313 0           my $code = <
1314             "" . (\$__s = "$text",
1315             EOM
1316 0           foreach (@ary) {
1317 0           my ($from, $to) = @$_;
1318 0           $code .= <
1319             \$__s =~ s/$from/$to/g,
1320             EOM
1321             }
1322 0           $code . ", \$__s)[-1]";
1323             }
1324              
1325             sub PictureN {
1326 0     0 0   my ($self, $param) = @_;
1327 0           my %opts;
1328 0           while ($param =~ s/^([ZF])//) {
1329 0           $opts{$1}++;
1330             }
1331 0 0         unless ($param =~ s/^\\\((.*?)\\\)//s) {
1332 0           $self->Syntax;
1333             }
1334 0           my $format = $1;
1335 0 0         unless ($param =~ s/^\\\.\\(["'])(.*?)\\\1$//s) {
1336 0           $self->Syntax;
1337             }
1338 0           my $text = $2;
1339 0           <
1340             "" . (\$__s = "$text" || !"$opts{'Z'}" ? sprintf("%${format}f", "$text") : " ",
1341             "$opts{'F'}" ? (\$__s =~
1342             s!(\\d+)!scalar(reverse join(\$HTML::Merge::Ini::THOUSAND_SEPARATOR || ",", (reverse \$1) =~ /(\\d{1,3})/g))!e) : undef,
1343             \$__s =~ s/\\./\$HTML::Merge::Ini::DECIMAL_SEPARATOR || '.'/e,
1344             \$__s)[-1]
1345             EOM
1346             }
1347              
1348             sub PictureA {
1349 0     0 0   my ($self, $param) = @_;
1350 0           my %opts;
1351 0           while ($param =~ s/^([LRCSPWDE])//) {
1352 0           $opts{$1}++;
1353             }
1354 0           foreach (qw(SCP DE)) {
1355 0           my $count;
1356 0           foreach (split(//)) {
1357 0 0 0       $self->Die("Illegal flag combinations")
1358             if ($opts{$_} && $count++);
1359             }
1360             }
1361 0 0         unless ($param =~ s/^\\\((.*?)\\\)//s) {
1362 0           $self->Syntax;
1363             }
1364 0           my $format = $1;
1365 0 0         unless ($param =~ s/^\\\.\\(["'])(.*?)\\\1$//s) {
1366 0           $self->Syntax;
1367             }
1368 0           my $text = $2;
1369 0           <
1370             "" . (\$__s = "$text",
1371             "$opts{'C'}" && \$__s =~ tr/a-z/A-Z/,
1372             "$opts{'S'}" && \$__s =~ tr/A-Z/a-z/,
1373             "$opts{'P'}" && \$__s =~ s/\\b([a-z]\\S+)/ucfirst(lc(\$1))/egi,
1374             "$opts{'L'}" && \$__s =~ s/^\\s+//,
1375             "$opts{'R'}" && \$__s =~ s/\\s+\$//,
1376             "$opts{'W'}" && \$__s =~ s/\\s{2,}/ /g,
1377             "$opts{'E'}" && (\$__s =~ s/([^ _A-Za-z0-9-\\/])/sprintf("%%%02X", ord(\$1))/ge, \$__s =~ s/ /+/g),
1378             "$opts{'D'}" && (\$__s =~ s/\\+/ /g, \$__s =~ s/%(..)/chr(hex(\$1))/ge),
1379             sprintf("%${format}s", \$__s))[-1]
1380             EOM
1381             }
1382              
1383             sub PictureD {
1384 0     0 0   my ($self, $param) = @_;
1385 0 0         unless ($param =~ s/^\\\((.*?)\\\)//s) {
1386 0           $self->Syntax;
1387             }
1388 0           my $format = $1;
1389 0 0         unless ($param =~ s/^\\\.\\(["'])(.*?)\\\1$//s) {
1390 0           $self->Syntax;
1391             }
1392 0           my $date = $2;
1393              
1394 0           <
1395             (require Time::Local,
1396             ("$date") =~ /^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})\$/,
1397             \$__t = Time::Local::timelocal(\$6, \$5, \$4, \$3, \$2 - 1, \$1 - 1900),
1398             HTML::Merge::Engine::time2str("$format", \$__t))[-1]
1399            
1400             EOM
1401             }
1402              
1403             sub PictureX {
1404 0     0 0   my ($self, $param) = @_;
1405 0 0         unless ($param =~ s/^\\\((.*?)\\\)//s) {
1406 0           $self->Syntax;
1407             }
1408 0           my $times = $1;
1409 0 0         unless ($param =~ s/^\\\.\\(["'])(.*?)\\\1$//s) {
1410 0           $self->Syntax;
1411             }
1412 0           my $text = $2;
1413 0           <
1414             (HTML::Merge::Engine::Force("$times", 'ui'),
1415             "$text" x "$times")[-1]
1416             EOM
1417             }
1418              
1419             sub DoINC {
1420 0     0 0   my ($self, $engine, $param) = @_;
1421 0 0         unless ($param =~ /^\\\.(.*?)(\\[+-]\d+)?$/s) {
1422 0           $self->Syntax;
1423             }
1424 0 0         my ($var, $step) = ($1, defined($2) ? $2 : 1);
1425 0           <
1426             HTML::Merge::Engine::Force("$step", "n");
1427             HTML::Merge::Engine::Force(\$vars{"$var"}, "n");
1428             \$vars{"$var"} += "$step";
1429             EOM
1430             }
1431              
1432             sub DoSTATE {
1433 0     0 0   my ($self, $engine, $param) = @_;
1434 0 0         $self->Syntax if $param;
1435 0           "\$engines{\"$engine\"}->State";
1436             }
1437              
1438             sub DoEMPTY {
1439 0     0 0   my ($self, $engine, $param) = @_;
1440 0 0         $self->Syntax if $param;
1441 0           "\$engines{\"$engine\"}->Empty";
1442             }
1443              
1444             sub DoMAIL {
1445 0     0 0   my ($self, $engine, $param) = @_;
1446 0 0         unless ($param =~ /^\\\.\\(['"])(.*?)\\\1\\([\.,])\\(['"])(.*?)\\\4(.*)$/s) {
1447 0           $self->Syntax;
1448             }
1449 0           my $del = quotemeta($3);
1450 0           my ($from, $to, $rem, $subject) = ($2, $5, $6);
1451 0 0         if ($rem) {
1452 0 0         unless ($rem =~ /^\\$del\\(['"])(.*?)\\\1$/s) {
1453 0           $self->Syntax;
1454             }
1455 0           $subject = $2;
1456             }
1457 0           $self->Push('mail', $engine);
1458 0           <
1459             \$__from = "$from";
1460             \$__from =~ s/^.*\<(.*)\>\$/\$1/;
1461             \$__from =~ s/^(.*?)\\s+\(\".*\"\)\$/\$1/;
1462             \$__to = "$to";
1463             \$__to =~ s/^.*\<(.*)\>\$/\$1/;
1464             \$__to =~ s/^(.*?)\\s+\(\".*\"\)\$/\$1/;
1465             use HTML::Merge::Mail;
1466             eval '\$__mail = OpenMail(\$__from, \$__to, \$HTML::Merge::Ini::SMTP_SERVER);';
1467              
1468             HTML::Merge::Error::HandleError('WARN', 'Mail failed: \$\@') if \$\@;
1469             \$__prev = select \$__mail;
1470              
1471             print "From: $from\\r\\n";
1472             print "To: $to\\r\\n";
1473             print "Subject: $subject\\r\\n";
1474             print "X-Mailer: Merge v. $VERSION (c) http://www.raz.co.il\\r\\n";
1475             print "\\r\\n";
1476             EOM
1477             }
1478             sub DoUnMAIL {
1479 0     0 0   my ($self, $engine, $param) = @_;
1480 0           $self->Expect($engine, 'mail');
1481 0           <
1482             eval ' CloseMail(\$__mail); ';
1483             HTML::Merge::Error::HandleError('WARN', 'Mail failed: \$\@') if \$\@;
1484             select \$__prev;
1485             EOM
1486             }
1487             #####################################
1488             sub DoDB
1489             {
1490 0     0 0   my ($self, $engine, $param) = @_;
1491            
1492 0           my ($type, $db, $host);
1493 0           my ($dsn,$dsn1, $user, $pass);
1494              
1495 0           $INTERNAL_DB="dbname=$HTML::Merge::Ini::MERGE_ABSOLUTE_PATH/merge.db";
1496              
1497 0 0         unless ($param =~ /^\\[\.=]\\(['"])(.*?)\\\1$/s)
1498             {
1499 0           $self->Syntax;
1500             }
1501              
1502 0           $dsn = $2;
1503 0           ($dsn1, $user, $pass) = split(/\s*\\,\s*/, $dsn);
1504              
1505 0 0         unless ($dsn1)
1506             {
1507 0           $self->Die("DSN not specified");
1508             }
1509              
1510 0           for($dsn)
1511             {
1512 0 0         if(/^SYSTEM$/)
1513             {
1514 0 0         if($HTML::Merge::Ini::SESSION_DB)
1515             {
1516 0           $type = $HTML::Merge::Ini::DB_TYPE;
1517 0           $db = $HTML::Merge::Ini::SESSION_DB;
1518 0           $host = $HTML::Merge::Ini::DB_HOST;
1519 0           $user = $HTML::Merge::Ini::DB_USER;
1520 0           $pass = $HTML::Merge::Ini::DB_PASSWORD;
1521             }
1522             else
1523             {
1524 0           $type=$INTERNAL_DB_TYPE;
1525 0           $db="$INTERNAL_DB";
1526             }
1527 0           last;
1528             }
1529 0 0         if(/^DEFAULT$/)
1530             {
1531 0           $type = $HTML::Merge::Ini::DB_TYPE;
1532 0           $db = $HTML::Merge::Ini::DB_DATABASE;
1533 0           $host = $HTML::Merge::Ini::DB_HOST;
1534 0           $user = $HTML::Merge::Ini::DB_USER;
1535 0           $pass = $HTML::Merge::Ini::DB_PASSWORD;
1536 0           last;
1537             }
1538             else
1539             {
1540 0           $dsn1 =~ s/^dbi\\://;
1541 0           ($type, $db, $host) = split(/\\:/, $dsn1);
1542 0 0         ($type, $db) = (undef, $type) unless ($db);
1543 0           last;
1544             }
1545             }
1546              
1547 0           <
1548             \$engines{"$engine"}->Preconnect("$type", "$db", "$host", "$user", "$pass");
1549             EOM
1550             }
1551             #####################################
1552             sub DoDISCONNECT {
1553 0     0 0   my ($self, $engine, $param) = @_;
1554 0 0         $self->Syntax if $param;
1555 0           qq!delete \$engines{"$engine"};!;
1556             }
1557              
1558             sub DoEXIT {
1559 0     0 0   my ($self, $engine, $param) = @_;
1560 0 0         $self->Die if $param;
1561 0           "die 'STOP_ON_ERROR';\n";
1562             }
1563              
1564             sub DoLOGIN {
1565 0     0 0   my ($self, $engine, $param) = @_;
1566 0 0         unless ($param =~ /^\\[\.=]\\(['"])(.*?)\\\1\\\,\\(['"])(.*?)\\\3$/s) {
1567 0           $self->Syntax;
1568             }
1569 0           my ($user, $pass) = ($2, $4);
1570 0           qq!\$engines{"$engine"}->Login("$user", "$pass")!;
1571             }
1572              
1573             sub DoCHPASS {
1574 0     0 0   my ($self, $engine, $param) = @_;
1575 0 0         unless ($param =~ /^\\[\.=]\\(['"])(.*?)\\\1$/s) {
1576 0           $self->Syntax;
1577             }
1578 0           qq!\$engines{"$engine"}->ChangePassword("$2");!;
1579             }
1580              
1581             sub DoAUTH {
1582 0     0 0   my ($self, $engine, $param) = @_;
1583 0 0         unless ($param =~ /^\\\.\\(['"])(.*?)\\\1$/s) {
1584 0           $self->Syntax;
1585             }
1586 0           qq!\$engines{"$engine"}->HasKey("$2")!;
1587             }
1588              
1589             sub DoADDUSER {
1590 0     0 0   my ($self, $engine, $param) = @_;
1591 0 0         unless ($param =~ /^\\[\.=]\\(['"])(.*?)\\\1\\\,\\(['"])(.*?)\\\3$/s) {
1592 0           $self->Syntax;
1593             }
1594 0           my ($user, $pass) = ($2, $4);
1595 0           qq!\$engines{"$engine"}->AddUser("$user", "$pass");!;
1596             }
1597              
1598             sub DoDELUSER {
1599 0     0 0   my ($self, $engine, $param) = @_;
1600 0 0         unless ($param =~ /^\\[=\.]\\(['"])(.*?)\\\1$/s) {
1601 0           $self->Syntax;
1602             }
1603 0           my ($user) = ($2);
1604 0           qq!\$engines{"$engine"}->DelUser("$user");!;
1605             }
1606              
1607             sub DoJOIN {
1608 0     0 0   my ($self, $engine, $param) = @_;
1609 0 0         unless ($param =~ /^\\[=\.]\\(['"])(.*?)\\\1\\\,\\(['"])(.*?)\\\3$/s) {
1610 0           $self->Syntax;
1611             }
1612 0           my ($user, $group) = ($2, $4);
1613 0           qq!\$engines{"$engine"}->JoinGroup("$user", "$group");!;
1614             }
1615              
1616             sub DoPART {
1617 0     0 0   my ($self, $engine, $param) = @_;
1618 0 0         unless ($param =~ /^\\[=\.]\\(['"])(.*?)\\\1\\\,\\(['"])(.*?)\\\3$/s) {
1619 0           $self->Syntax;
1620             }
1621 0           my ($user, $group) = ($2, $4);
1622 0           qq!\$engines{"$engine"}->PartGroup("$user", "$group");!;
1623             }
1624              
1625             sub DoGRANT {
1626 0     0 0   my ($self, $engine, $param) = @_;
1627 0 0         unless ($param =~ /^\\[=\.]([UG])\\\.\\(['"])(.*?)\\\2\\\,\\(['"])(.*?)\\\4$/si) {
1628 0           $self->Syntax;
1629             }
1630 0           my ($how, $who, $realm) = (uc($1), $3, $5);
1631 0 0         if ($how eq 'U') {
1632 0           return qq!\$engines{"$engine"}->GrantUser("$who", "$realm");!;
1633             }
1634 0 0         if ($how eq 'G') {
1635 0           return qq!\$engines{"$engine"}->GrantGroup("$who", "$realm");!;
1636             }
1637             }
1638              
1639             *DoREVOKE = \&DoEVOKE;
1640              
1641             sub DoEVOKE {
1642 0     0 0   my ($self, $engine, $param) = @_;
1643 0 0         unless ($param =~ /^\\[=\.]([UG])\\\.\\(['"])(.*?)\\\2\\\,\\(['"])(.*?)\\\4$/si) {
1644 0           $self->Syntax;
1645             }
1646 0           my ($how, $who, $realm) = (uc($1), $3, $5);
1647 0 0         if ($how eq 'U') {
1648 0           return qq!\$engines{"$engine"}->RevokeUser("$who", "$realm");!;
1649             }
1650 0 0         if ($how eq 'G') {
1651 0           return qq!\$engines{"$engine"}->RevokeGroup("$who", "$realm");!;
1652             }
1653             }
1654              
1655             sub DoATTACH {
1656 0     0 0   my ($self, $engine, $param) = @_;
1657 0 0         unless ($param =~ /^\\[=\.]\\(['"])(.*?)\\\1\\\,\\(['"])(.*?)\\\3$/s) {
1658 0           $self->Syntax;
1659             }
1660 0           my ($template, $subsite) = ($2, $4);
1661 0           qq!\$engines{"$engine"}->Attach("$template", "$subsite");!;
1662             }
1663              
1664             sub DoDETACH {
1665 0     0 0   my ($self, $engine, $param) = @_;
1666 0 0         unless ($param =~ /^\\[=\.]\\(['"])(.*?)\\\1\\\,\\(['"])(.*?)\\\3$/s) {
1667 0           $self->Syntax;
1668             }
1669 0           my ($template, $subsite) = ($2, $4);
1670 0           qq!\$engines{"$engine"}->Detach("$template", "$subsite");!;
1671             }
1672              
1673              
1674             *DoREQUIRE = \&DoEQUIRE;
1675              
1676             sub DoEQUIRE {
1677 0     0 0   my ($self, $engine, $param) = @_;
1678 0 0         unless ($param =~ /^\\[=\.]\\(['"])(.*?)\\\1\\\,\\(['"])(.*?)\\\3$/s) {
1679 0           $self->Syntax;
1680             }
1681 0           my ($template, $realms) = ($2, $4);
1682 0           qq!\$engines{"$engine"}->Require("$template", "$realms");!;
1683             }
1684              
1685             sub DoUSER {
1686 0     0 0   my ($self, $engine, $param) = @_;
1687 0 0         $self->Syntax if $param;
1688 0           qq!\$engines{"$engine"}->GetUser!;
1689             }
1690              
1691             sub DoNAME {
1692 0     0 0   my ($self, $engine, $param) = @_;
1693 0 0         $self->Syntax if $param;
1694 0           qq!scalar(\$engines{"$engine"}->GetUserName)!;
1695             }
1696              
1697             sub DoTAG {
1698 0     0 0   my ($self, $engine, $param) = @_;
1699 0 0         $self->Syntax if $param;
1700 0           qq!(\$engines{"$engine"}->GetUserName)[1]!;
1701             }
1702              
1703             sub DoMERGE {
1704 0     0 0   my ($self, $engine, $param) = @_;
1705 0 0         $self->Syntax if $param;
1706 0           '"$HTML::Merge::Ini::MERGE_PATH/$HTML::Merge::Ini::MERGE_SCRIPT"';
1707             }
1708              
1709             sub DoTEMPLATE {
1710 0     0 0   my ($self, $engine, $param) = @_;
1711 0 0         $self->Syntax if $param;
1712 0           qq!\$HTML::Merge::template!;
1713             }
1714              
1715             sub DoTRANSFER {
1716 0     0 0   my ($self, $engine, $param) = @_;
1717 0           my $validate;
1718 0 0         unless ($param =~ s/^\\\.(.+)$//s) {
1719 0           $self->Syntax;
1720             }
1721 0           qq!qq//!;
1722             }
1723              
1724             sub DoSUBMIT {
1725 0     0 0   my ($self, $engine, $param) = @_;
1726 0           my $validate;
1727 0 0         if ($param =~ s/^\\\.\\(["'])(.*)\\\1$//s) {
1728 0           $validate = " onSubmit=\"$2\"";
1729             }
1730 0 0         $self->Syntax if $param;
1731 0           $self->Push('submit', $engine);
1732 0           <
1733             print qq!
1734             !;
1735             EOM
1736             }
1737              
1738             sub DoUnSUBMIT {
1739 0     0 0   my ($self, $engine, $param) = @_;
1740 0           $self->Expect($engine, 'submit');
1741 0           qq!print "\\n";!;
1742             }
1743              
1744             sub DoDECIDE {
1745 0     0 0   my ($self, $engine, $param) = @_;
1746 0 0         unless ($param =~ /^\\\.\\(['"])(.*?)\\\1\\\?\\(['"])(.*?)\\\3\\\:\\(['"])(.*?)\\\5$/s) {
1747 0           $self->Syntax;
1748             }
1749 0           my ($decision, $true, $false) = ($2, $4, $6);
1750 0           <
1751             (
1752             (eval("$decision") ? "$true" : "$false"),
1753             \$@ && HTML::Merge::Error::HandleError('ERROR', \$@)
1754             )[0]
1755             EOM
1756             }
1757              
1758             sub DoDATE {
1759 0     0 0   my ($self, $engine, $param) = @_;
1760 0           my $delta = 0;
1761 0 0         if ($param =~ s/^\\[,\.]((?:\\-)?\d+)$//s) {
1762 0           $delta = $1;
1763             }
1764 0 0         $self->Syntax if $param;
1765 0           <
1766             (HTML::Merge::Engine::Force("$delta", 'i'),
1767             \@__t = localtime(time + "$delta" * 3600 * 24),
1768             sprintf("%04d" . ("%02d" x 5), \$__t[5] + 1900, \$__t[4] + 1,
1769             \@__t[reverse (0 .. 3)]))[-1]
1770             EOM
1771             }
1772              
1773             sub DoDAY {
1774 0     0 0   my ($self, $engine, $param) = @_;
1775 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1776 0           $self->Syntax;
1777             }
1778 0           qq{substr("$2", 6, 2) * 1};
1779             }
1780              
1781             sub DoMONTH {
1782 0     0 0   my ($self, $engine, $param) = @_;
1783 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1784 0           $self->Syntax;
1785             }
1786 0           qq{substr("$2", 4, 2) * 1};
1787             }
1788              
1789             sub DoYEAR {
1790 0     0 0   my ($self, $engine, $param) = @_;
1791 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1792 0           $self->Syntax;
1793             }
1794 0           qq{substr("$2", 0, 4)};
1795             }
1796              
1797             sub DoMINUTE {
1798 0     0 0   my ($self, $engine, $param) = @_;
1799 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1800 0           $self->Syntax;
1801             }
1802 0           qq{substr("$2", 10, 2) * 1};
1803             }
1804              
1805             sub DoHOUR {
1806 0     0 0   my ($self, $engine, $param) = @_;
1807 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1808 0           $self->Syntax;
1809             }
1810 0           qq{substr("$2", 8, 2) * 1};
1811             }
1812              
1813              
1814             sub DoSECOND {
1815 0     0 0   my ($self, $engine, $param) = @_;
1816 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1817 0           $self->Syntax;
1818             }
1819 0           qq{substr("$2", 12, 2) * 1};
1820             }
1821              
1822             sub DoDATEDIFF {
1823 0     0 0   my ($self, $engine, $param) = @_;
1824 0 0         unless ($param =~ /^\\\.([HSMD])\\\.(\\['"])?(.*)\2\\,(\\['"])?(.*)\4$/s) {
1825 0           $self->Syntax;
1826             }
1827 0           my ($how, $before, $now) = ($1, $3, $5);
1828 0           my %hash = qw(S 1 M 60 H 3600 D 86400);
1829 0   0       my $div = $hash{$how} || 1;
1830 0           <
1831             (require Time::Local,
1832             \$__conv = sub { (shift() =~ /^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})/);
1833             Time::Local::timelocal(\$6, \$5, \$4, \$3, \$2 - 1, \$1 - 1900); },
1834             int((&\$__conv("$now") - &\$__conv("$before")) / $div))[-1]
1835             EOM
1836             }
1837              
1838             sub DoDATE2UTC {
1839 0     0 0   my ($self, $engine, $param) = @_;
1840 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1841 0           $self->Syntax;
1842             }
1843 0           <
1844             (require Time::Local,
1845             ("$2") =~ /^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})\$/,
1846             Time::Local::timelocal(\$6, \$5, \$4, \$3, \$2 - 1, \$1 - 1900))[-1]
1847             EOM
1848             }
1849              
1850             sub DoUTC2DATE {
1851 0     0 0   my ($self, $engine, $param) = @_;
1852 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/) {
1853 0 0         $self->Syntax if $param;
1854             }
1855 0           <
1856             (HTML::Merge::Engine::Force("$2", 'ui'),
1857             \@__t = localtime("$2"),
1858             sprintf("%04d" . ("%02d" x 5), \$__t[5] + 1900, \$__t[4] + 1,
1859             \@__t[reverse (0 .. 3)]))[-1]
1860             EOM
1861             }
1862              
1863             sub DoLASTDAY {
1864 0     0 0   my ($self, $engine, $param) = @_;
1865 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1866 0           $self->Syntax;
1867             }
1868 0           <
1869             ((\$__y, \$__m, \$__d) = ("$2" =~ /^(\\d{4})(\\d{2})(\\d{2})/),
1870             \$__base = (qw(31 28 31 30 31 30 31 31 30 31 30 31))[\$__m - 1],
1871             \$__leap = (\$__y % 4) ? 0
1872             : ((\$__y % 100) ? 1
1873             : ((\$__y % 400) ? 0 : 1)
1874             ),
1875             \$__base + (\$__m == 2 ? \$__leap : 0))[-1]
1876             EOM
1877             }
1878              
1879             sub DoADDDATE {
1880 0     0 0   my ($self, $engine, $param) = @_;
1881 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1\\\,\\(['"])(.*)\\\3\\,\\(['"])(.*)\\\5\\,\\(['"])(.*)\\\7$/s) {
1882 0           $self->Syntax;
1883             }
1884 0           my ($date, $d, $m, $y) = ($2, $4, $6, $8);
1885 0           <
1886             (require Time::Local,
1887             ("$date") =~ /^(\\d{4})(\\d{2})(\\d{2})(\\d{2})(\\d{2})(\\d{2})/,
1888             \$__t = Time::Local::timelocal(\$6, \$5, \$4, \$3, \$2 - 1, \$1 - 1900)
1889             + 3600 * 24 * "$d",
1890             \@__t = localtime(\$__t),
1891             \$__t[4] += "$m", \$__t[5] += "$y",
1892             \$__t[5] += int(\$__t[4] / 12), \$__t[4] %= 12,
1893             sprintf("%04d" . ("%02d" x 5), \$__t[5] + 1900, \$__t[4] + 1,
1894             \@__t[reverse (0 .. 3)]))[-1]
1895             EOM
1896             }
1897              
1898             sub DoDIVERT {
1899 0     0 0   my ($self, $engine, $param) = @_;
1900 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1901 0           $self->Syntax;
1902             }
1903 0           my $fn = $2;
1904 0           $self->Push('divert', $engine);
1905 0           <
1906             push(\@__diverts, select);
1907             use Symbol;
1908             \$__sym = gensym;
1909             open(\$__sym, ">>/tmp/merge-\$\$-$fn.divert") || die \$!;
1910             select \$__sym;
1911             push(\@HTML::Merge::cleanups, eval qq!sub { unlink "/tmp/merge-\$\$-$fn.divert" }!);
1912             EOM
1913             # Value of $fn might contain merge variables, that might change
1914             # until cleanup time. Therefore compile cleanup function
1915             # with the filename as part of the source.
1916             }
1917              
1918             sub DoUnDIVERT {
1919 0     0 0   my ($self, $engine, $param) = @_;
1920 0 0         $self->Syntax if $param;
1921 0           $self->Expect($engine, 'divert');
1922 0           <
1923             \$__sym = select;
1924             select pop \@__diverts;
1925             close \$__sym;
1926             EOM
1927             }
1928              
1929             sub DoDUMP {
1930 0     0 0   my ($self, $engine, $param) = @_;
1931 0 0         unless ($param =~ /^\\\.\\(['"])(.*)\\\1$/s) {
1932 0           $self->Syntax;
1933             }
1934 0           my $fn = $2;
1935 0           <
1936             (open(DIVERT_DUMP, "/tmp/merge-\$\$-$fn.divert") || die(\$!), join("", ),
1937             close(DIVERT_DUMP))[1]
1938             EOM
1939             }
1940              
1941             *DoCGET = *DoCVAR = \&DoCOOKIE;
1942              
1943             sub DoCOOKIE {
1944 0     0 0   my ($self, $engine, $param) = @_;
1945 0 0         unless ($param =~ s/^\\\.(.*)$//s) {
1946 0           $self->Syntax;
1947             }
1948 0           "\$engines{\"$engine\"}->GetCookie(\"$1\")";
1949             }
1950              
1951             *DoCSET = \&DoCOOKIESET;
1952              
1953             sub DoCOOKIESET {
1954 0     0 0   my ($self, $engine, $param) = @_;
1955 0 0         unless ($param =~ s/^\\\.(.*?)\\=\\(['"])(.*?)\\\2((?:\\,.*)?)$//s) {
1956 0           $self->Syntax;
1957             }
1958 0           my $expire = substr($4, 2);
1959 0           "\$engines{\"$engine\"}->SetCookie(\"$1\", eval(\"$3\"), \"$expire\");";
1960             }
1961              
1962             sub DoSOURCE {
1963 0     0 0   my ($self, $engine, $param) = @_;
1964 0           my $file = '$HTML::Merge::template';
1965 0 0         if ($param =~ s/^\\\.\\(['"])(.*)\\\1$//s) {
1966 0           $file = $2;
1967             }
1968 0 0         $self->Syntax if $param;
1969 0           $self->Push('source', $engine);
1970 0           qq!' 1971             HTML::Merge::Development::MakeLink('printsource.pl', "template=$file")
1972             . '" TITLE="view source">'!;
1973             }
1974              
1975             sub DoUnSOURCE {
1976 0     0 0   my ($self, $engine, $param) = @_;
1977 0           $self->Expect($engine, 'source');
1978 0           qq!""!;
1979             }
1980              
1981             sub safecreate {
1982 0     0 0   my @tokens = split(/\//, shift);
1983 0           pop @tokens;
1984 0           my $dir;
1985 0           foreach (@tokens) {
1986 0           $dir .= "/$_";
1987 0           mkdir $dir, 0755;
1988             }
1989             }
1990             #####################################
1991             sub CompileFile
1992             {
1993 0     0 0   my ($file, $out, $sub) = @_;
1994              
1995 0           my $tmp;
1996 0 0         open(I, $file) || die "Cannot open $file: $!";
1997 0           my $text = join("", );
1998 0           close(I);
1999            
2000 0 0         open(O, ">$out") || die "Can't write $out: $!";
2001 0           my $prev = select O;
2002            
2003 0 0         unless ($sub) {
2004 0           print $Config{'startperl'}, "\n";
2005 0           print <<'EOM';
2006             use HTML::Merge::Engine;
2007             use HTML::Merge::Error;
2008             no strict;
2009             sub getvar ($) {
2010             $vars{shift()};
2011             }
2012             sub setvar ($$) {
2013             $vars{$_[0]} = $_[1];
2014             }
2015             sub incvar ($$) {
2016             $vars{$_[0]} += $_[1];
2017             }
2018             sub getfield ($;$) {
2019             my ($field, $engine) = @_;
2020             $engines{$engine}->Var($field);
2021             }
2022             sub merge ($) {
2023             my $code = shift;
2024             require HTML::Merge::Compile;
2025             my $text;
2026             eval { $text = HTML::Merge::Compile::Compile($code, __FILE__); };
2027             HTML::Merge::Error::HandleError('ERROR', $@) if $@;
2028             eval $text;
2029             HTML::Merge::Error::HandleError('ERROR', $@) if $@;
2030             }
2031             sub dbh () {
2032             $engines{""}->{'dbh'};
2033             }
2034             sub register ($) {
2035             push(@HTML::Merge::cleanups, shift);
2036             }
2037              
2038             if (tied(%engines)) {
2039             undef %engines;
2040             untie %engines;
2041             }
2042              
2043             tie %engines, HTML::Merge::Engine;
2044             use CGI qw/:standard/;
2045             @keys = param();
2046             %vars = ();
2047             foreach (@keys) {
2048             $vars{$_} = param($_);
2049             }
2050             =line
2051             $tmp = HTML::Merge::Compile::CgiParse();
2052             foreach (keys(%$tmp))
2053             {
2054             print "$_\t:\t",$tmp->{$_},"\n";
2055             }
2056              
2057             %vars = %$tmp;
2058             =cut
2059             unless ($HTML::Merge::Ini::TEMPLATE_CACHE) {
2060            
2061             EOM
2062 0           print "\t\trequire '$HTML::Merge::config';\n\t}\n";
2063             }
2064              
2065 0           eval {
2066 0           print &Compile($text, $file);
2067             };
2068 0           my $code = $@;
2069            
2070 0 0         unless ($sub) {
2071 0           print <<'EOM';
2072             HTML::Merge::Engine::DumpSuffix;
2073             untie %engines;
2074              
2075             1;
2076             EOM
2077             }
2078              
2079 0           select $prev;
2080 0           close(O);
2081 0 0         die $code if $code;
2082 0           chmod 0755, $out;
2083            
2084             }
2085              
2086             sub Syntax {
2087 0     0 0   my $self = shift;
2088 0           &DB::Syntax($self);
2089             }
2090              
2091              
2092             package DB;
2093              
2094             sub Syntax {
2095 0     0 0   my $self = shift;
2096 0           my $step = 0;
2097 0           my $sub;
2098 0           my $pkg = ref($self);
2099 0           for (;;) {
2100 0           $step++;
2101 0           my @c = caller($step);
2102 0           $sub = $c[3];
2103 0 0 0       last if $sub =~ s/^(.*)::Do// && UNIVERSAL::isa($self, $1);
2104             }
2105 0           $self->Die("Syntax error on $sub: $DB::args[2]");
2106             }
2107              
2108              
2109             package HTML::Merge::Ext;
2110              
2111             sub Macro {
2112 0     0 0   my $text = shift;
2113 0           $text =~ s/(?
2114              
2115 0           $HTML::Merge::Ext::COMPILER->Macro($text);
2116 0           return "";
2117             }
2118              
2119             1;