File Coverage

blib/lib/Text/TemplateFill.pm
Criterion Covered Total %
statement 242 305 79.3
branch 67 126 53.1
condition 20 45 44.4
subroutine 23 28 82.1
pod 9 21 42.8
total 361 525 68.7


line stmt bran cond sub pod time code
1             # This module generates text output from template files, filling in value fields.
2             # /\
3             # / \ (C) Copyright 2002-2003 Parliament Hill Computers Ltd.
4             # \ / All rights reserved.
5             # \/
6             # . Author: Alain Williams, July 2002
7             # . addw@phcomp.co.uk
8             # .
9             # .
10             #
11             # SCCS: @(#)TemplateFill.pm 1.7 03/27/03 10:27:28
12             # Alain D D Williams , July 2002
13             #
14             # This module is free software; you can redistribute it and/or modify
15             # it under the same terms as Perl itself. You must preserve this entire copyright
16             # notice in any use or distribution.
17             # The author makes no warranty what so ever that this code works or is fit
18             # for purpose: you are free to use this code on the understanding that any problems
19             # are your responsibility.
20              
21             # Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is
22             # hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and
23             # this permission notice appear in supporting documentation.
24              
25 1     1   648 use strict;
  1         2  
  1         42  
26              
27             package Text::TemplateFill;
28              
29 1     1   6 use Exporter;
  1         1  
  1         34  
30 1     1   885 use POSIX;
  1         7469  
  1         12  
31 1     1   15861 use Math::Expression;
  1         5078  
  1         217  
32              
33             # What local variables - visible elsewhere
34 1         101 use vars qw/
35             @ISA @EXPORT
36 1     1   9 /;
  1         1  
37            
38             @ISA = ('Exporter');
39              
40             @EXPORT = qw(
41             $VERSION
42             );
43              
44             our $VERSION = "1.7";
45              
46             # This contains the current (and probably only) template instance.
47             # It is used where we can't guess it, this relies on only being invoked for one
48             # instance at a time:
49             my $globalself;
50             my $globaltag; # And for current tag
51              
52             my $ArithIdent; # Identification string for arithmetic - in case of errors
53              
54             # Nothing at startup
55 1     1   9736 BEGIN {
56             ;
57             }
58              
59             # Nothing at end
60 1     1   0 END {
61             ;
62             }
63              
64             # Default error output function
65             sub PrintError {
66 0     0 0 0 printf STDERR @_;
67 0         0 print STDERR "\n";
68             }
69              
70             # Called when something is wrong.
71             # Args: $self, fprintf style args
72             # The point is that the error function is called.
73             # Nasty bit to avoid reporting functions in this module.
74             # Take care to switch back to the calling locale - if we set one.
75             sub Error {
76 0     0 0 0 my $self = shift @_;
77 0         0 my ($pack,$file,$line,$sub,$hargs,undef,$eval,$require);
78 0         0 my $i = 1;
79 0         0 do {
80 0         0 ($pack,$file,$line,$sub,$hargs,undef,$eval,$require) = caller($i++);
81             } while($pack eq 'Text::TemplateFill');
82              
83 0         0 my $fmt = shift @_;
84 0         0 my $OldLocale;
85              
86 0 0       0 if($self->{OldLocale} ne '') {
87 0         0 $OldLocale = setlocale(LC_CTYPE);
88 0         0 setlocale(LC_ALL, $self->{OldLocale});
89             }
90              
91 0         0 $self->{ErrorFunction}($fmt . ". Called from $file line $line fn: $pack ", @_);
92              
93 0 0       0 setlocale(LC_ALL, $OldLocale) if(defined($OldLocale));
94              
95 0         0 $self->{Errors}++;
96             }
97              
98             # Print errors from arithmetic, rely on $globalself
99             # Prepend the offending statement at the start of the error message, makes it a bit long I am afraid:
100             sub ArithError {
101 0     0 0 0 my $fmt = shift @_;
102 0         0 &Error($globalself, "Calc '%s' " . $fmt, $ArithIdent, @_);
103             }
104              
105             # Return the value of a variable - return an array
106             # 0 Magic value to Math::Expression
107             # 1 Variable name
108             # NB: Math::Expression holds vars as arrays, this uses simple scalars.
109             sub ArithVarGet {
110 20     20 0 461 my ($self, $name) = @_;
111              
112 20         36 my $varref = &Value($globalself, $globaltag, $name, 0);
113 20         28 my @v = ( );
114 20 50       41 push @v, ${$varref} if(defined($varref));
  20         32  
115              
116 20         76 return @v;
117             }
118              
119             # Is a variable is defined - return 1 or 0
120             # 0 Magic value to Math::Expression
121             # 1 Variable name
122             sub ArithVarIsDef {
123 0     0 0 0 my ($self, $name) = @_;
124              
125 0         0 my $varref = &Value($globalself, $globaltag, $name, 0);
126              
127 0 0       0 return defined($varref) ? 1 : 0;
128             }
129              
130             # Set the value of a variable - return the array
131             # 0 Magic value to Math::Expression
132             # 1 Variable name
133             # 2 Value - array
134             sub ArithVarSet {
135 24     24 0 699 my ($self, $name, @val) = @_;
136              
137 24         49 my $varref = &Value($globalself, $globaltag, $name, 1);
138              
139 24         35 ${$varref} = $val[$#val];
  24         36  
140              
141 24         116 return @val;
142             }
143              
144             # Create a new template object.
145             # Initialise default options.
146             sub new {
147 2     2 0 251 my $class = shift;
148              
149 2         5 my $PageNo = 0; # Not started yet. The first to print is page 1
150 2         2 my $PageLineNo = 0;
151 2         9 my $Now = time;
152              
153             # Program variables
154 2         4 my %ProgVars = (
155             );
156              
157             # Calculated & auto vars
158 2         12 my %CalcVars = (
159             'PageNo' => \$PageNo, # Current page number
160             'PageLineNo' => \$PageLineNo, # Current line number in page (0 if page not started)
161             'Now' => \$Now, # Current time - actually time of 'new'.
162             );
163              
164 2         14 my $CalcHandle = new Math::Expression;
165 2         71 $CalcHandle->SetOpt( 'VarGetFun' => \&ArithVarGet,
166             'VarSetFun' => \&ArithVarSet,
167             'VarIsDefFun' => \&ArithVarIsDef,
168             'PrintErrFunc' => \&ArithError);
169              
170             # References to all the paragraphs for this template
171             # This contains references to hashes keyed on the paragraph tag.
172 2         47 my %Paragraphs = (
173             );
174              
175 2         30 my %template = (
176             'Errors' => 0, # Error count
177             'ErrorFunction' => \&PrintError, # What to call on error
178             'Initialised' => 0, # True when post-read file initialisation done
179             'Locale' => '', # May be something like 'fr_CA.ISO8859-1'
180             'OldLocale' => '', # On entry to GeneratePara
181             'BaseDir' => '.', # What to add if no '/' at start of file name
182             'LineTerminator'=> "\n", # What to put at the end of each line
183             'EndPageSeq' => '', # Probably \f, else use many empty lines
184             'PageLen' => 66, # Length of output page
185             'Variables' => \%ProgVars, # Hash of all program variables
186             'CalcVars' => \%CalcVars, # Hash of all calculated variables
187             'Paragraphs' => \%Paragraphs, # Hash of all paragraphs
188             'StartPageTag' => ' ', # Which tag to use to auto start a page, undef doesn't work - space is dodge
189             'EndPageTag' => ' ', # Which tag to use to auto end a page
190             'CalcHandle' => $CalcHandle, # For calculations
191             );
192              
193 2         41 return bless \%template => $class;
194             }
195              
196             # Set an option in the %template.
197             sub SetOpt {
198 2     2 1 17 my $self = shift @_;
199              
200 2         7 while($#_ > 0) {
201 6 50       21 &Error($self, "Unknown option '$_[0]'") unless(defined($self->{$_[0]}));
202 6 50       11 &Error($self, "No value to option '$_[0]'") unless(defined($_[1]));
203 6         9 $self->{$_[0]} = $_[1];
204 6         7 shift;shift;
  6         15  
205             }
206             }
207              
208             # Read a file in. The user arguments are:
209             # * a tag name that is used to identify/obtain this paragraph in the future
210             # * a file name that will be read, if this is not given, use tag.
211             # Return true on error.
212             sub ReadPara {
213 10     10 1 45 my ($self, $tag, $fname) = @_;
214              
215 10         15 $globalself = $self;
216              
217 10         77 $self->{Initialised} = 0; # Need to reassess what is what
218              
219 10 50       21 $fname = $tag unless(defined($fname)); # no $fname ?
220              
221 10         17 my @Lines = ();
222 10         12 my ($ParaOnPage, $ParaTotal) = (0, 0);
223 10         13 my @Calc = ();
224 10         12 my @CalcStr = ();
225 10         11 my @LineNoMap = ();
226              
227 10         76 my %ParaDescript = (
228             'Lines' => \@Lines,
229             'LineNoMap' => \@LineNoMap, # For error messages - else removed comments cause bad reporting of errors
230             'Calc' => \@Calc, # Calculations - parsed trees
231             'CalcStr' => \@CalcStr, # Calculations - uncompiled
232             'EndPage' => 0, # True if paragaph ends a page
233             'StartPage' => 0, # True if paragaph starts a page
234             'BlanksAfter' => 0, # True blanks to page botton when EndPage come after paragraph
235             'ParaOnPage' => \$ParaOnPage, # Paragraph usage count this page
236             'ParaTotal' => \$ParaTotal, # Paragraph usage count total
237             );
238              
239             # Get the file to open, prepend the base dir if not absolute:
240 10 50       41 my $fn = (($fname =~ /^\//) ? '' : $self->{BaseDir}) . '/' . $fname;
241              
242 10 50       358 unless(open(TMPL, "<$fn")) {
243 0         0 &Error($self, "Cannot open '%s' as: $!", $fn);
244 0         0 return(1);
245             }
246              
247 10         162 while() {
248 287         377 chop; # Basic line tidy:
249 287         11556 s/\s*\r?$//;
250 287 100       962 next if(/^\$\{#\}/);
251              
252             # If it is a calculation, extract it & save
253 189 100       351 if(/^\$\{Calc\s+(.*)\}$/) {
254 17         64 push @CalcStr, "$fn:$. '$1'";
255 17         80 push @Calc, $self->{CalcHandle}->Parse("$1");
256 17         2489 next;
257             }
258              
259             # If not an option, append to template lines:
260 172 100       344 unless(/^\$\{Opt\s/) {
261 154         236 push @Lines, $_;
262 154         254 push @LineNoMap, $.;
263 154         474 next;
264             }
265              
266             # Process options:
267 18 50       92 unless(/^\$\{Opt\s+(\w+)\s*([^\s]+)?\s*\}/) {
268 0         0 &Error($self, "Bad option line $. in '%s'", $fn);
269 0         0 next;
270             }
271              
272 18         46 my ($optkey, $optval) = ($1, $2);
273              
274             # The option will be held as a member of a hash, find which hash
275 18         18 my $href;
276 18 100       49 $href = \%ParaDescript if(defined($ParaDescript{$optkey}));
277 18 100       42 $href = $self if(defined($self->{$optkey}));
278 18 50       35 unless(defined($href)) {
279 0         0 &Error($self, "Unknown option '$optkey' line $. in '%s'", $fn);
280 0         0 next;
281             }
282              
283             # No validation on the value, if none just set to true
284 18 50       87 $href->{$optkey} = defined($optval) ? ($optval eq "''" ? '' : $optval) : 1;
    100          
285             }
286              
287 10         113 close(TMPL);
288              
289 10         31 $self->{Paragraphs}{$tag} = \%ParaDescript;
290              
291 10         40 return(0);
292             }
293              
294             # This checks what has been read & deduces:
295             # * What tag to use at the start of a page
296             # * What tag to use to end a page
297             sub CompleteInit {
298 2     2 0 4 my $self = $_[0];
299            
300 2         3 foreach my $para (keys %{$self->{Paragraphs}}) {
  2         7  
301              
302 10         11 my $parh = ${$self->{Paragraphs}}{$para};
  10         21  
303              
304             # NB: there is an important difference between the tag having a space value & having the empty value.
305             # The space value is a dodge to say that it is unset - ugh, empty means deliberately no tag.
306 0         0 $self->{StartPageTag} = $para if($self->{StartPageTag} eq ' ' and
307 10 50 33     109 ${$parh}{'StartPage'} != 0);
308 0         0 $self->{EndPageTag} = $para if($self->{EndPageTag} eq ' ' and
309 10 50 33     30 ${$parh}{'EndPage'} != 0);
310             }
311              
312 2         6 $self->{Initialised} = 1;
313             }
314              
315             # Reset all page/line counters to zero.
316             sub Reset {
317 4     4 1 531 my $self = $_[0];
318              
319 4         7 ${$self->{CalcVars}{PageLineNo}} = 0;
  4         11  
320 4         6 ${$self->{CalcVars}{PageNo}} = 0;
  4         9  
321              
322             # Reset paragraph usage on this page to 0
323 4         5 foreach my $para (keys %{$self->{Paragraphs}}) {
  4         16  
324 20         74 ${$self->{Paragraphs}{$para}{ParaOnPage}} = 0;
  20         38  
325 20         23 ${$self->{Paragraphs}{$para}{ParaTotal}} = 0;
  20         43  
326             }
327             }
328              
329             # This is called to start a page.
330             # Args: $self, optional tag to start the page with - else the defined start tag - if there is one
331             # This assumes that any previous page is complete.
332             # Return something to print
333             sub StartPage {
334 4     4 1 7 my ($self, $tag) = @_;
335              
336             # Need this in case called before a GeneratePara
337 4 50       12 &CompleteInit($self) unless($self->{Initialised});
338              
339 4 50       16 $tag = $self->{StartPageTag} unless(defined($tag));
340              
341 4         6 ${$self->{CalcVars}{PageLineNo}} = 1; # Line number of first line to print
  4         8  
342 4         6 ${$self->{CalcVars}{PageNo}}++;
  4         8  
343              
344             # Reset paragraph usage on this page to 0
345 4         5 foreach my $para (keys %{$self->{Paragraphs}}) {
  4         13  
346 20         19 ${$self->{Paragraphs}{$para}{ParaOnPage}} = 0;
  20         46  
347             }
348              
349             # If there is a start page tag, output it:
350 4 50 33     31 return(&GeneratePara($self, $tag)) if($tag ne ' ' and $tag ne '');
351 0         0 return('');
352             }
353              
354             # End the current page.
355             # Args: $self, optional tag to end the page with - else the defined end tag - if there is one
356             # If there is nothing on the page, print an empty page with a footer.
357             sub EndPage {
358 4     4 1 8 my ($self, $tag) = @_;
359              
360 4         5 my $text = '';
361             # Page not started ? Get it going but don't print a header:
362 4 50       4 $text = &StartPage($self, '') if(${$self->{CalcVars}{PageLineNo}} < 1);
  4         25  
363              
364 4 50       10 $tag = $self->{EndPageTag} unless(defined($tag));
365              
366             # Work out how empty many lines we must generate to put the footer in the right place.
367 4         8 my $lines = $self->{PageLen} - ${$self->{CalcVars}{PageLineNo}};
  4         9  
368              
369             # If end of page tag: blank line down to it (or after), else o/p a formfeed or blank down to end of page:
370 4 50       16 if($tag ne '') {
371 4         8 my $blanksafter = $self->{Paragraphs}{$tag}{BlanksAfter};
372 4         7 my $blanklines = $self->{LineTerminator} x ($lines - $#{$self->{Paragraphs}{$tag}{Lines}});
  4         15  
373 4 50       10 $text .= $blanklines unless($blanksafter);
374 4 50 33     22 $text .= &GeneratePara($self, $tag) if($tag ne ' ' and $tag ne '');
375 4 50       14 $text .= $blanklines if($blanksafter);
376             } else {
377 0 0       0 $text .= ($self->{EndPageSeq} ne '') ? $self->{EndPageSeq} : ($self->{LineTerminator} x $lines);
378             }
379              
380 4         6 ${$self->{CalcVars}{PageLineNo}} = 0; # Next page not started
  4         9  
381              
382 4         31 return $text;
383             }
384              
385             # Print an end page if there is something on the current page.
386             # Args: $self, optional tag to end the page with - else the defined end tag - if there is one
387             # Don't print anything if the current page has not been started - should only be at start of file.
388             # If a tag is specified and it is not the default end page tag, a check will be made to see if the
389             # specified paragraph will fit on the page, if not a standard endpage/startpage is first done.
390             sub CompletePage {
391 4     4 1 50 my ($self, $tag) = @_;
392              
393 4 50       6 return '' if(${$self->{CalcVars}{PageLineNo}} < 1);
  4         16  
394              
395 4         6 my $text = '';
396              
397             # Won't fit
398 4         9 $text = &EndPage($self) if(defined($tag) and $tag ne $self->{EndPageTag} and $self->{PageLen} > 0 and
399 4 50 33     51 ${$self->{CalcVars}{PageLineNo}} + $#{${$self->{Paragraphs}{$tag}}{Lines}} >= $self->{PageLen});
  4   33     6  
  4   33     23  
400              
401 4         12 return $text . &EndPage($self, $tag);
402             }
403              
404             # Evaluate the paragraph and return an array that can be printed.
405             # Return the empty string
406             sub GeneratePara {
407 18     18 1 621 my ($self, $tag) = @_;
408 18         35 my $para = $self->{Paragraphs}{$tag};
409 18         20 my $text = '';
410              
411 18         26 $self->{OldLocale} = '';
412              
413             # print "GeneratePara '$tag' self='$self'\n";
414 18 50       39 unless(defined($para)) {
415 0         0 &Error($self, "Tag '%s' is not known", $tag);
416 0         0 return('');
417             }
418 18         18 my $lines = ${$para}{Lines};
  18         30  
419              
420 18 100       43 &CompleteInit($self) unless($self->{Initialised}); # Once off after files read
421              
422             # Need to end the page if the current paragraph will not fit on the page - NOT if printing EOP para
423             # Count lines left, lines this para, lines in EndPage. Not if Line/page < 1
424 14         24 $text .= &EndPage($self) if($para->{EndPage} == 0 and $self->{PageLen} > 0 and
425 14         39 ${$self->{CalcVars}{PageLineNo}} + $#{$lines} +
  14         71  
426 18 50 66     93 ($self->{EndPageTag} ne '' ? $#{${$self->{Paragraphs}{$self->{EndPageTag}}}{Lines}} : 0) >= $self->{PageLen});
  14 50 66     15  
427              
428             # Need to start a page and this paragraph is not a start of page paragraph ?
429             # Even if $tag is a StartPage we need to call &StartPage to get page # increment, etc.
430 18 100       24 if(${$self->{CalcVars}{PageLineNo}} <= 0) {
  18         51  
431 4 50       15 $text .= &StartPage($self, ($para->{StartPage} == 0 ? undef : $tag));
432 4 50       49 return $text if($para->{StartPage}); # Else we get the start page text twice
433             }
434              
435 14         17 ${$para->{ParaOnPage}}++;
  14         18  
436 14         15 ${$para->{ParaTotal}}++;
  14         20  
437              
438             # Perform any calculations first:
439 14         20 $globalself = $self; # For arithmetic
440 14         17 $globaltag = $tag;
441 14         20 my $calc = $para->{Calc};
442 14 50       15 &Calculate($self, $tag, $calc, $para->{CalcStr}) if($#{$calc} >= 0);
  14         47  
443              
444             # Change locale if one is defined
445 14 50       40 if($self->{Locale} ne '') {
446 14         85 $self->{OldLocale} = setlocale(LC_CTYPE);
447 14         750 setlocale(LC_ALL, $self->{Locale});
448             }
449              
450 14         26 my $lineno = 0; # For error messages
451 14         15 foreach my $line (@{$lines}) {
  14         28  
452 244         253 my $exp_line = &Expand($self, $para, $line, $tag, ${$para}{LineNoMap}[$lineno]);
  244         555  
453 244         600 $text .= $exp_line . $self->{LineTerminator};
454 244         227 ${$self->{CalcVars}{PageLineNo}}++;
  244         411  
455 244         361 $lineno++;
456             }
457              
458             # Change locale back
459 14 50       112 setlocale(LC_ALL, $self->{OldLocale}) if($self->{OldLocale} ne '');
460 14         49 $self->{OldLocale} = '';
461              
462 14         60 return $text;
463             }
464              
465             # Perform calculations for a paragraph
466             sub Calculate {
467 14     14 0 24 my ($self, $tag, $calc, $calcstr) = @_;
468 14         27 my $para = $self->{Paragraphs}{$tag};
469              
470 14         18 for(my $i = 0; $i <= $#{$calc}; $i++) {
  38         168  
471 24         24 my $tree = ${$calc}[$i];
  24         32  
472 24         32 $ArithIdent = ${$calcstr}[$i]; # For errors
  24         36  
473 24         77 $self->{CalcHandle}->EvalToScalar($tree);
474             }
475             }
476              
477             # Return the value of a variable or constant.
478             # This is for use in expression evaluation.
479             # NB: $self is for Math::Expression, so rely on $globalself.
480             # undefOK is set if we are assigning.
481             sub Value {
482 44     44 0 65 my ($self, $tag, $val, $undefOK) = @_;
483 44         83 my $para = $globalself->{Paragraphs}{$tag};
484              
485 44         46 my ($tn, $vn);
486 44 50       82 if($val =~ /^\$(\w+)\.?(\w+)?/) {
487 0         0 ($tn, $vn) = ($1, $2);
488             } else {
489 44         69 ($tn, $vn) = ($val, undef);
490             }
491              
492 44         115 my ($varref, $varname) = &GetVarDets($globalself, $tag, $tn, $vn, "Bad expression value in paragraph '$tag'", $undefOK);
493              
494 44         96 return $varref;
495             }
496              
497             # Expand a line - internal function.
498             sub Expand {
499 244     244 0 394 my ($self, $para, $line, $tag, $lineno) = @_;
500 244         260 my $newline = '';
501              
502             # print "expand '$line'\n";
503             # Extract ${value@conversion%format}
504              
505             # This is nasty:
506 244         815 while($line =~ s/^
507             ([^\$]*) # Any non dollar
508             \$\{(\w+)\.?(\w+)? # ${Variable or ${Tag.Variable
509             (@(\w+)\s*(<([^>]+)>)?)? # Opt: @conversion
510             (%[-+ #]*\d*.?\d*\w+)? # %PrintfFormat
511             \}//x) {
512 104         194 $newline .= $1;
513 104         151 my $tn = $2;
514 104         142 my $vn = $3;
515 104         121 my $conv = $5;
516 104         123 my $conv_opt = $7;
517 104         127 my $format = $8;
518              
519 104         297 my ($substv, $varname) = &GetVarDets($self, $tag, $tn, $vn, "Line $lineno of paragraph '$tag'", 0);
520              
521 104 50       208 unless(defined($substv)) {
522 0         0 $newline .= $varname; # Couldn't get a value for variable
523 0         0 next;
524             }
525              
526             # Special conversions:
527 104 100 66     235 if(defined($conv) and $conv ne '') {
528 12 50       21 if($conv eq 'time') {
    0          
529             # Convert using a date style format string
530 12 50       57 $conv_opt = '%c' unless(defined($conv_opt)); # Locale preferred conversion
531              
532 12         15 my $newval = strftime($conv_opt, localtime ${$substv});
  12         618  
533 12         27 $substv = \$newval;
534             } elsif($conv eq 'center') {
535             # Center a field in a specified width
536 0 0 0     0 $conv_opt = 1 unless(defined($conv_opt) and ($conv_opt =~ /^\d+/));
537 0         0 my $len = length(${$substv});
  0         0  
538              
539 0 0       0 if($len < $conv_opt) {
540 0         0 my $newval = (' ' x (($conv_opt - $len) / 2)) . ${$substv} . (' ' x (($conv_opt - $len + 1) / 2));
  0         0  
541 0         0 $substv = \$newval;
542             }
543             } else {
544 0         0 &Error($self, "Line $lineno of paragraph '%s' uses unknown conversion '%s'", $tag, $conv);
545 0         0 $newline .= '${' . $vn . '@' . $conv . '}';
546 0         0 next;
547             }
548             }
549              
550             # print "substv='$substv' vn='$vn' format='$format'\n";
551             # Format
552 104 100 66     370 if(defined($format) and $format ne '') {
553             # If numeric avoid barf on unassigned var:
554 68 50 33     63 if(${$substv} eq '' and ($format =~ /[duoxegfXEGiDUOF]$/)) {
  68         188  
555 0         0 my $z = 0;
556 0         0 $substv = \$z;
557             }
558 68         80 $newline .= sprintf $format, ${$substv};
  68         556  
559             } else {
560 36         34 $newline .= ${$substv};
  36         161  
561             }
562             }
563              
564 244         542 return $newline . $line;
565             }
566              
567             # Find out things about a variable and return:
568             # * the reference to the variable, undefined on error
569             # * the variable name, in a form suitable for error display
570             # Args:
571             # 0 $self
572             # 1 Paragraph tag
573             # 2 tagname -- extracted from tagname.varname
574             # 3 varname
575             # 4 Error message prefix
576             # 5 True if undefined value is OK (in which case set the empty string) (must be a calc var)
577             sub GetVarDets {
578 148     148 0 245 my ($self, $tag, $tn, $vn, $msg, $undefOK) = @_;
579 148         259 my $para = $self->{Paragraphs}{$tag};
580              
581 148         140 my $varref;
582             my $varname;
583              
584             # If no Variable, the Tag is the variable
585 148 100       223 if(defined($vn)) {
586 8         14 $varname = $tn . '.' . $vn;
587              
588 8 50       18 unless(defined($self->{Paragraphs}{$tn})) {
589 0         0 &Error($self, "%s uses unknown tag '%s' in template variable '%s'", $msg, $tag, $varname);
590             } else {
591 8 50       25 unless(defined($varref = $self->{Paragraphs}{$tn}{$vn})) {
592 0 0       0 if($undefOK) {
593 0         0 my $empty = '';
594 0         0 $varref = $self->{Paragraphs}{CalcVars}{$vn} = \$empty;
595             } else {
596 0         0 &Error($self, "%s uses unknown template variable '%s'", $msg, $varname);
597             }
598             }
599             }
600             } else {
601             # Get ref to value - page's own, calculated or global:
602 140         227 $varref = $self->{Variables}{$tn};
603 140 100       376 $varref = $self->{CalcVars}{$tn} if(defined($self->{CalcVars}{$tn}));
604 140 50       282 $varref = $para->{$tn} if(defined($para->{$tn}));
605 140 100 66     304 if(!defined($varref) and $undefOK) {
606 7         12 my $empty = '';
607 7         17 $varref = $self->{CalcVars}{$tn} = \$empty;
608             }
609 140         167 $varname = $tn;
610 140 50       278 &Error($self, "%s uses unknown template variable '%s'", $msg, $varname)
611             unless(defined($varref));
612             }
613              
614             # Is the value of the variable defined ?
615 148 50       142 unless(defined(${$varref})) {
  148         304  
616 0         0 &Error($self, "%s uses variable '%s' which does not have a value", $msg, $varname);
617 0         0 undef $varref;
618             }
619              
620 148         467 return ($varref, ('${' . $varname . '}'));
621             }
622              
623             # Set variable_name/variable association.
624             # The argument is an array of name => variable_reference
625             # Because we take a reference, this only needs to be called once in a program.
626             # A warning will be made if a name is reused.
627             sub BindVars {
628 2     2 1 23 my $self = shift @_;
629              
630 2         5 my $varp = $self->{Variables};
631              
632 2   66     20 while((my $name = shift @_) and (my $val = shift @_)) {
633             # print "name='$name' val='${$val}'\n";
634 32 50       69 if(defined($varp->{$name})) {
635 0         0 &Error($self, "Reusing variable name '%s'", $name);
636 0         0 $self->{Errors}--; # Only warning
637             }
638 32         180 $varp->{$name} = $val;
639             }
640             }
641              
642             # Remove variable_name/variable association.
643             # The argument is an array of names
644             sub UnbindVars {
645 0     0 1   my $self = shift @_;
646              
647 0           my $varp = $self->{Variables};
648              
649 0           while((my $name = shift @_)) {
650             # print "name='$name' val='${$val}'\n";
651 0 0         if(defined($varp->{$name})) {
652 0           delete($varp->{$name});
653             } else {
654 0           &Error($self, "Ubinding unknown variable name '%s'", $name);
655 0           $self->{Errors}--; # Only warning
656             }
657             }
658             }
659              
660             1;
661              
662             __END__