File Coverage

blib/lib/IO/ReadPreProcess.pm
Criterion Covered Total %
statement 591 654 90.3
branch 329 468 70.3
condition 97 147 65.9
subroutine 28 34 82.3
pod 10 25 40.0
total 1055 1328 79.4


line stmt bran cond sub pod time code
1             package IO::ReadPreProcess;
2              
3             # The idea is to provide an 'intelligent' bottom end read function for scripts.
4             # Read lines, process .if/.else/.fi, do .include .let .print - and more.
5             # It provides IO::Handle-ish functions to slot in easily to most scripts.
6              
7             # Author: Alain D D Williams March 2015, 2016, 2017 Copyright (C) the author.
8             # SCCS: @(#)ReadPreProcess.pm 1.14 08/15/17 23:19:49
9              
10 1     1   18411 use 5.006;
  1         3  
11 1     1   5 use strict;
  1         1  
  1         21  
12 1     1   4 use warnings;
  1         5  
  1         25  
13             #use Data::Dumper;
14 1     1   4 use IO::File;
  1         2  
  1         94  
15 1     1   260 use IO::Pipe;
  1         876  
  1         32  
16              
17             our $errstr; # Error string
18              
19 1     1   6 use Math::Expression;
  1         1  
  1         4540  
20              
21             our $VERSION = 0.85;
22              
23             # Control directive recognised by getline():
24             my %ctlDirectives = map { $_ => 1 } qw/ break case close continue do done echo else elseif elsif endswitch error eval exit
25             fi for if include last let local next noop out print read return set sub switch test unless until while /;
26              
27             # Directives that can be used in a condition:
28             my %condDirectives = map { $_ => 1 } qw/ include read test /;
29              
30             # Need to test for this ... all except first line
31             my %forbidden = map { $_ => 1 } qw/ function sub /;
32              
33             # Block pairs: start & ends
34             my %blkPairs = qw/ sub done while done until done for done if fi unless fi /;
35             my %endings = map { $_ => 1 } qw/ done fi /;
36             my %loops = map { $_ => 1 } qw/ while until for /;
37             my %makeExpr = map { $_ => 1 } qw/ let if unless elsif elseif while until for /;
38             my %options = map { $_ => 1 } qw/ trace /;
39              
40             # Math variables (others see POD below):
41             # _FileNames - array of open file names
42             # _LineNumbers - array of open file names
43             # _IncludeDepth - how many files open
44             # _FileName _LineNumber - current ones
45             # The arrays are to allow the generation of a traceback.
46              
47             # Properties not described in new
48             # Information about the current file is kept as references so that it can be pushed down a stack (think: .include) and popped when it is closed.
49             #
50             # Lines contains refs else we would need to update before pushing
51             sub new
52             {
53 61     61 1 72222 my $class = shift;
54 61         1046 my $self = bless {
55             FrameStk => [], # Frames
56             Frame => undef, # Reference to current frame (last in FrameStk)
57              
58             subs => {}, # Keys are known sub
59             Streams => {}, # Input streams
60              
61             out => "", # Name of current output stream, empty string is return line to program
62              
63             # Public properties:
64             MaxLoopCount => 50, # Max times that a loop can go round
65             Raw => 0, # Return input as it is seen
66             PipeOK => 0, # True if allowed to open a pipe
67             Trim => 1, # Trim input lines
68             OnError => 'warn', # Can set to: warn, die, ''
69             OutStreams => {}, # Key: name, value: Stream
70              
71             Place => '??', # Last read location: current file/line#
72              
73             DirStart => '.', # Directive start sequence
74             TopIsFd => 0, # First file pre-opened, ie Fd passed to open()
75             Error => 0, # Set true on error, functions then just return undef
76              
77             trace => 0, # 1 trace directives, 2 trace generated input
78             @_
79             }, $class;
80              
81             # These output streams are given for free:
82 61 50       480 $self->{OutStreams}->{STDOUT} = *STDOUT{IO} unless( defined($self->{OutStreams}->{STDOUT}));
83 61 50       243 $self->{OutStreams}->{STDERR} = *STDERR{IO} unless( defined($self->{OutStreams}->{STDERR}));
84              
85             # Produce an escaped version of the directive start string. All the RE special prefix with a backslash.
86             # This will be used at the start of an RE but we want it taken literally.
87 61 50       225 unless(defined($self->{DirStartRE})) {
88 61         154 $self->{DirStartRE} = $self->{DirStart};
89 61         498 $self->{DirStartRE} =~ s/([\$.()\[\]*+?\\^|])/\\$1/g;
90             }
91             # This is not worth it:
92             # $self->{dirLineRE} = qr/^($self->{DirStartRE})(\w*)\s*(.*)/;
93             # $self->{commentRE} = qr/^$self->{DirStartRE}#/;
94              
95             $self->{Math} = new Math::Expression(PermitLoops => 1, EnablePrintf => 1)
96 61 50       625 unless(defined $self->{Math});
97              
98 61 50       3585 unless($self->{Math}->{VarHash}->{_Initialised}) {
99 61         226 $self->{Math}->ParseToScalar('_FileNames := EmptyList; _LineNumbers := EmptyList; _IncludeDepth := 0; _Initialised := 1');
100 61         75425 $self->{Math}->ParseToScalar('_ARGS := (); _ := _EOF := 0');
101 61         47892 $self->{Math}->ParseToScalar('_CountGen := _CountSkip := _CountDirect := _CountFrames := _CountOpen := 0');
102             }
103              
104             # We do some things a lot - compile them, that is the expensive part:
105 61         53439 my %math = (
106             SetLineMath => '_LineNumbers[-1] := _LineNumber',
107             m_openFrame => 'push(_FileNames, _FileName); push(_LineNumbers, _LineNumber); ++_IncludeDepth',
108             m_closeFrame => 'pop(_FileNames); pop(_LineNumbers); --_IncludeDepth; _FileName := ""; _LineNumber := 0; if(count(_FileNames)){_FileName := _FileNames[-1]; _LineNumber := _LineNumbers[-1]}',
109             );
110              
111 61         368 while (my($p, $e) = each %math) {
112 183         157628 $self->{$p} = $self->{Math}->Parse($e);
113             }
114              
115             # Take some out - cause problems if there since we try to set them again
116 61         89446 my %opts;
117 61         219 for (qw/ Fd File /) {
118 122 100       326 if(defined($self->{$_})) {
119 63         228 $opts{$_} = $self->{$_};
120 63         187 delete $self->{$_};
121             }
122             }
123              
124             # Attempt to open the file if passed:
125 61 50       196 if(defined($opts{File})) {
126 61 100       180 if(defined($opts{Fd})) {
127             # Already open, note name, push to include stack:
128 2         11 $self->openFrame(binmode => '', %opts, Name => $opts{File}, LineNumber => 0, Generate => 1, ReturnFrom => 1);
129             } else {
130 59 50       318 return undef unless($self->open(binmode => '', %opts));
131             }
132             }
133              
134             $self
135 61         319 }
136              
137             # Open a file. Args:
138             # * File - a name - mandatory
139             # * Fd - a file handle that it is already open on - optional
140             # Return $self on OK, undef on error
141             # Pushes the current file on a stack that allows restore by close()
142             sub open
143             {
144 59     59 1 142 my $self = shift;
145 59         279 my %args = @_;
146              
147             return $self->SetError('open() must be given File argument', 1)
148 59 50       204 if( !defined $args{File});
149              
150             # But elsewhere File is called Name - which could be a sub name
151 59         149 $args{Name} = $args{File};
152 59         103 delete $args{File};
153              
154             # Get it open on $self->{Fd}
155 59         91 my $Fd;
156 59 50       156 if( !defined $args{Fd}) {
157 59 50       251 return undef unless($Fd = $self->openFile($args{Name}));
158             } else {
159             # Use already opened Fd
160 0         0 $Fd = $args{Fd};
161 0         0 $self->{TopIsFd} = 1;
162             }
163              
164 59         361 $self->openFrame(%args, Fd => $Fd, LineNumber => 0, Generate => 1, ReturnFrom => 1);
165             }
166              
167             # Open a file, apply any binmode, return FD
168             sub openFile
169             {
170 89     89 0 254 my ($self, $name) = @_;
171 89         152 my $Fd;
172              
173 89 50       791 return $self->SetError("Open of file '$name' failed: $! at $self->{Place}")
174             unless($Fd = IO::File->new($name, 'r'));
175              
176 89         11235 $self->{Math}->{VarHash}->{_CountOpen}->[0]++;
177              
178 89 50       347 $Fd->binmode($self->{Frame}->{binmode}) if($self->{Frame}->{binmode});
179              
180 89         310 $Fd
181             }
182              
183             # Internal routine.
184             # Assign the new values and push onto their own stack so that, after a later open()
185             # it can be popped by a close() of that later file.
186             sub openFrame
187             {
188 465     465 0 803 my $self = shift;
189 465         2313 my %args = @_;
190 465         1007 my $vh = $self->{Math}->{VarHash};
191              
192             # Create the new frame:
193 465         2151 my %f = ( PushedInput => [], @_ );
194              
195             # Create var _ARGS if argument Args:
196 465 100       1199 if(defined($args{Args})) {
197 200         682 $f{LocalVars} = {_ARGS => $vh->{_ARGS}};
198 200         489 delete $vh->{_ARGS};
199 200         379 $vh->{_ARGS} = $args{Args};
200 200         366 delete $f{Args};
201             }
202              
203             # One of Code or Fd must be passed
204             # Must have the following, if not set copy from enclosing frame:
205 465         1056 for my $p (qw/ Code CodeLine Name LineNumber Fd Generate binmode /) {
206              
207 3255 100       6104 $f{$p} = $args{$p} if(defined($args{$p}));
208              
209 3255 100 100     9178 if(defined($self->{Frame}) && defined($self->{Frame}->{$p})) {
210 2306 100       5876 $f{$p} = $self->{Frame}->{$p} unless(defined($f{$p}));
211             }
212             }
213              
214 465         1615 $f{FrameStart} = "$f{Name}:$f{LineNumber}";
215              
216 465         660 push(@{$self->{FrameStk}}, \%f);
  465         1313  
217 465         1023 $self->{Frame} = \%f;
218              
219 465         902 $vh->{_CountFrames}->[0]++;
220 465         1078 $vh->{_LineNumber}->[0] = $f{LineNumber};
221 465         913 $vh->{_FileName}->[0] = $f{Name};
222 465         1787 $self->{Math}->EvalToScalar($self->{m_openFrame});
223              
224              
225 465         132519 $self; # success
226             }
227              
228             # Close a file - this might mean closeing more than 1 frame
229             # An important block is a (.include) file.
230             # Check $self->{Fd} for there being an open file.
231             # Return false on error
232             sub close
233             {
234 130     130 1 216 my $self = shift;
235              
236             # Unwind until we find a ReturnFrom:
237 130         197 my $rf;
238 130         199 do {
239 134         270 $rf = $self->{Frame}->{ReturnFrom};
240             return undef
241 134 50       343 unless($self->closeFrame);
242             } until($rf);
243              
244 130         383 $self
245             }
246              
247             # Closes the current frame, pops back the previous one - if there was one
248             sub closeFrame
249             {
250 465     465 0 733 my $self = shift;
251              
252             return $self->SetError("Cannot close when there is not a frame open", 1)
253 465 50 66     1500 unless(defined $self->{Frame}->{Code} or defined $self->{Frame}->{Fd});
254              
255             # If error: this will be an OS level error
256 465 50       1077 return $self->SetError("IO::File I/O error: $!")
257             if($self->error);
258              
259             # Don't close - we don't want to close STDIN, could cause problems
260             # Rely on the IO::File object for files that we have opened ourselves being unreferenced and thus closed.
261             # $self->{Fd}->close;
262              
263             # Pop back the previous state/file - if there was one
264             # Pop the description/state for the file just closed and assign
265             # state for the file just revealed - what is now at the top of the stack:
266              
267             # Pop any local vars:
268 465 100       1137 if($self->{Frame}->{LocalVars}) {
269 202         393 my $alist = $self->{Frame}->{LocalVars};
270 202         337 my $vh = $self->{Math}->{VarHash};
271 202         1003 while (my ($k, undef) = each %$alist) {
272 454         903 delete($vh->{$k});
273 454 100       1614 $vh->{$k} = $alist->{$k} if(defined($alist->{$k}));
274             }
275             }
276              
277              
278 465         608 my $old = pop(@{$self->{FrameStk}});
  465         985  
279 465         925 $self->{Frame} = $self->{FrameStk}->[-1];
280              
281 465 100 100     1507 $self->{Frame}->{CodeLine} = $old->{CodeLine} if($self->{Frame}->{Code} && $old->{CpMove});
282              
283             # Get arith variables in sync
284 465         1834 $self->{Math}->EvalToScalar($self->{m_closeFrame});
285              
286 465         272987 $self
287             }
288              
289             # This package is intended to read text files - so straight binmode is prob not wanted.
290             # But binmode is also used to allow different encoding - eg :utf8
291             # Return true on success, on error undef with error in $!
292             # Record the mode in the frame, inherited by child frames
293             sub binmode
294             {
295 0     0 1 0 my ($self, $mode) = @_;
296              
297 0 0       0 return $self->SetError("binmode: a file has not been opened", 1) unless $self->{Frame}->{Fd};
298              
299 0         0 $self->{Frame}->{binmode} = $mode;
300              
301             $self->{Frame}->{Fd}->binmode($mode) # Pass the call straight down
302 0 0       0 unless(ref $self->{Frame}->{Fd} eq 'ARRAY');
303             }
304              
305             # Return 1 if the next read will return EOF or the file is not open:
306             sub eof
307             {
308 0     0 1 0 my $self = shift;
309 0 0       0 return 1 unless($self->{Fd});
310 0 0       0 return !@{$self->{Fd}} if(ref $self->{Fd} eq 'ARRAY');
  0         0  
311 0         0 $self->{Fd}->eof;
312             }
313              
314             # Get the name of the file to open
315             # Args:
316             # * name
317             # * just return undef if cannot open, don't print error - optional
318             # First process escapes
319             # If it starts '$', the next word is a variable, use the value(s) like $PATH - search
320             # Else resolve
321             sub ResolveFilename
322             {
323 32     32 0 77 my ($self, $name, $noerr) = @_;
324              
325             # If it starts '$'
326 32 100       109 if(substr($name, 0, 1) eq '$') {
327 5 50       48 return $self->SetError("Bad syntax include file name: '$name' at $self->{Place}", 1)
328             unless($name =~ m:^\$(\w+)(/.+)$:i);
329 5         22 my ($var, $rest) = ($1, $2);
330              
331 5         11 my ($pt, @val);
332             return $self->SetError("Bad expression in include: '$name' at $self->{Place}", 1)
333 5 50 33     24 unless(($pt = $self->{Math}->Parse($var)) && (@val = $self->{Math}->Eval($pt)));
334              
335             # Search down path:
336 5         1190 for my $pref (@val) {
337 12         50 my $fp = $self->GetPath("$pref$rest");
338 12 50       33 return undef unless $fp;
339 12 100       336 return $fp # Grab it if it exists
340             if(-e $fp);
341             }
342              
343 0 0       0 return undef if($noerr);
344              
345 0         0 return $self->SetError("Cannot find a file in search '$name'. $var='@val' at $self->{Place}", 1)
346             }
347              
348             # Plain file name:
349 27         97 return $self->GetPath($name);
350             }
351              
352             # If it is absolute (starts '/'): accept
353             # If it starts '#' it is relative to the process's CWD, remove '#' & accept
354             # The rest are relative to the current file name: prepend any directory name
355             # Don't try to canonicalise a/b/../c to a/c - think symlinks.
356             sub GetPath
357             {
358 39     39 0 90 my ($self, $name) = @_;
359              
360             # Absolute path:
361 39 100       137 return $name if index($name, '/') == 0;
362              
363             # Relative to our CWD:
364 35 100       95 if(substr($name, 0, 1) eq '#') {
365 9         29 $name = substr($name, 1); # Remove #
366 9         27 $name = substr($name, 1) # Remove / after #
367             while(substr($name, 0, 1) eq '/');
368 9         28 return $name;
369             }
370              
371             # Everything else is relative to the current file
372              
373             # Cannot have a relative name if the current file was passed as Fd
374             return $self->SetError("Cannot include file relative to file descriptor. '$name' at $self->{Place}", 1)
375 26 50 33     84 if($self->{TopIsFd} && @{$self->{FrameStk}} == 1);
  0         0  
376             # **** This refers to self->file - on stack & called Name
377              
378             # Find the last opened file name
379 26         36 my $last;
380             return undef
381 26 50       78 unless($last = $self->GetLastFileName);
382              
383             # Note RE ensures that $currDir is '' if $last does not contain '/':
384 26         202 my ($currDir) = $last =~ m:^(.*?/?)[^/]+$:;
385              
386 26         113 $currDir . $name
387             }
388              
389             # Get the name of the last file opened, dig down the stack
390             sub GetLastFileName
391             {
392 26     26 0 60 my ($self) = @_;
393              
394 26         37 my $frames = @{$self->{FrameStk}};
  26         60  
395              
396 26         88 while(--$frames >= 0) {
397 48 100       185 return $self->{FrameStk}->[$frames]->{Name} if(exists($self->{FrameStk}->[$frames]->{Fd}));
398             }
399              
400 0         0 return $self->SetError("Cannot find previous file name at $self->{Place}", 1);
401             }
402              
403             # Line parsed for escapes: \0 \e \v{varname}. varname is: /\w+/i
404             # Arg is a string that is processed & returned
405             sub ProcEscapes
406             {
407 2866     2866 0 4834 my ($self, $arg) = @_;
408              
409 2866         4024 my $ret = '';
410              
411 2866         7590 while($arg =~ s/^([^\\]*)\\(.)//) {
412 840         2081 $ret .= $1;
413 840 50       2705 if($2 eq '0') {
    100          
    50          
414             ; # nothing
415             } elsif($2 eq 'e') {
416 1         5 $ret .= '\\';
417             } elsif($2 eq 'v') {
418 839 50       3178 return $self->SetError("Invalid escape \\v$arg at $self->{Place}", 1)
419             unless($arg =~ s/^{(\w+|\w+\[\w+\])}//i);
420 839         1756 my $vn = $1;
421 839         2448 my $vv = $self->{Math}->ParseToScalar($1);
422 839 50       144502 return $self->SetError("Invalid variable in \\v{$1} at $self->{Place}", 1)
423             unless(defined($vv));
424 839         3048 $ret .= $vv;
425             } else {
426 0         0 return $self->SetError("Invalid escape \\$2 at $self->{Place}", 1);
427             }
428             }
429 2866 50       5087 return $self->SetError("Trailing \\ on line at $self->{Place}", 1)
430             if($arg =~ /\\/);
431              
432 2866         7986 $ret . $arg;
433             }
434              
435             # Split the argument string on spaces into an array of strings.
436             # If a portion starts with a quote, it may contain a space
437             # If $doEsc each result is processed by ProcEscapes()
438             # Return the result or false
439             sub SplitArgs
440             {
441 598     598 0 1359 my ($self, $arg, $doEsc) = @_;
442 598         872 my @args = ();
443              
444 598         2026 $arg =~ s/^\s*//;
445 598         1612 while($arg ne '') {
446 2474         4255 my $c1 = substr($arg, 0, 1);
447 2474 100 100     6758 if($c1 eq '"' or $c1 eq "'") {
448             # Extract the string delimited by quotes
449 10 50       113 return $self->SetError("Bad quoted string at $self->{Place}", 1)
450             unless($arg =~ s/^(["'])((\\{2})*|(.*?[^\\](\\{2})*))\1\s*//);
451 10         29 my $m = $2;
452 10         25 $m =~ s/\\([\\'"])/$1/g; # Remove embedded escapes, eg: \" => "
453 10         29 push(@args, $m);
454             } else {
455 2464         6515 $arg =~ s/^(\S+)\s*//;
456 2464         6878 push(@args, $1);
457             }
458              
459             }
460              
461 598 100       1573 @args = map { $self->ProcEscapes($_) } @args if($doEsc);
  2416         4259  
462              
463             @args
464 598         2012 }
465              
466             # Read & store a sub or function to hash in $self->{subs}->{Name}
467             # Don't start a frame since we are just reading this in
468             # Return true if OK
469             sub readSub
470             {
471 38     38 0 131 my ($self, $direc, $InLine, $arg) = @_;
472              
473             # Check that $self->{Frame}->{Fd} is an open file
474              
475 38         78 my $code = { };
476              
477 38         95 my @args;
478              
479 38 50       99 return $self->SetError("Missing $direc name at $self->{Place}", 1) unless($arg ne '');
480              
481             # Also need to check that name & args are IDs
482 38 50       171 return undef unless(@args = $self->SplitArgs($arg, 0));
483              
484             # First is the name:
485 38         111 $code->{Name} = shift @args;
486             return $self->SetError("Error: bad sub name '$code->{Name}' at $self->{Place}")
487 38 50       202 unless($code->{Name} =~ /^\w+$/);
488              
489             return $self->SetError("Error: Redefinition of sub '$code->{Name}' at $self->{Place}")
490 38 50       147 if(exists($self->{subs}->{$code->{Name}}));
491              
492 38         174 $self->{subs}->{$code->{Name}} = $code;
493 38         81 $code->{ArgNames} = @args;
494              
495             # sub args can have names:
496 38 100       107 $code->{ArgNames} = \@args if(@args);
497              
498 38         84 $code->{Block} = $direc; # Info only
499              
500 38         157 $self->ReadBlock($InLine, $code);
501              
502 38         67 $code->{LastLine}--; # Remove .done
503 38         57 $code->{FirstLine}++; # Remove .sub
504              
505 38         135 1
506             }
507              
508             # $direct is while/until/for
509             # $arg is loop condition/rest-of-line
510             # May start: -innn to specify max iterations
511             # **** THINKS ****
512             # Loops are found in 2 ways:
513             # (1) Reading from a {Fd} - ie in getline()
514             # (2) When in a sub or an enclosing loop
515             # We always buffer a loop, so the only difference is where/how it is found
516             # The purpose of this sub is for case (1), need to initiate a buffer creation
517             # If (1) read into a buffer/code and return a ref to the code
518             # If (2) set up $code and return that
519             sub ReadLoop
520             {
521 17     17 0 56 my ($self, $direc, $InLine, $arg) = @_;
522              
523 17         36 my $frame = $self->{Frame};
524              
525 17         55 my $code = { Block => $direc };
526              
527 17         77 $self->ReadBlock($InLine, $code);
528              
529 17         49 $code
530             }
531              
532             # Read a block (sub or loop) to arg \%code
533             # If this finds a loop - note it as within what we read -- works for sub & nested loops
534             # $InLine is the line just read
535             sub ReadBlock
536             {
537 55     55 0 166 my ($self, $InLine, $code) = @_;
538              
539             # Record where this was found:
540 55         251 my $h={ FileName => $self->{Frame}->{Name}, FileLine => $self->{Frame}->{LineNumber}};
541 55         289 while (my($k,$v)= (each %$h)){
542 110         411 $code->{$k} = $v;
543             }
544              
545 55         259 $code->{start} = "$code->{FileName}:$code->{FileLine}";
546              
547 55         142 my $started = "started $code->{start}";
548 55         91 my @blocks;
549              
550 55         89 my $frame = $self->{Frame};
551 55         99 my $lineNo; # when reading existing array
552              
553 55         121 $code->{FirstLine} = 0;
554 55         173 $code->{Lines} = [];
555              
556 55         121 my $lineCnt = 0;
557              
558 55         94 while(1) {
559              
560 566         1599 my $line = { Txt => $InLine, '#' => $. };
561              
562             # Quick return if it cannot be a directive line - or one that we recognise
563             # If not generating - skip to next
564 566 100 66     3530 unless($InLine =~ /^($self->{DirStartRE})(\w+)\s*(.*)/ and
      66        
565             (defined($ctlDirectives{$2}) or defined($self->{subs}->{$2}))) {
566 219 50       458 push @{$code->{Lines}}, $line unless(defined $frame->{Code});
  219         457  
567 219         311 $lineCnt++;
568             } else {
569              
570 347         695 my $leadin = $1; # String that identified the directive
571 347         537 my $dir = $2; # Directive
572 347         904 my $arg = $3; # Its argument
573              
574 347 100       664 if(exists $loops{$dir}) {
575             # Loop buster:
576 41         83 my $max = $self->{MaxLoopCount};
577 41 100       126 $max = $1 if($arg =~ s/^-i\s*(\d+)\s*//);
578 41         92 $line->{LoopMax} = $max;
579              
580             # Get loop condition:
581 41 50       110 return $self->SetError("Missing $dir condition at $self->{Place}", 1) unless($arg ne '');
582 41         61 my $cond = $arg;
583 41         114 $line->{Not} = $dir eq 'until';
584              
585 41 100       93 if($dir eq 'for') {
586             # Break out, 3 expressions, preserve trailing ones
587 14         83 my @e = split /;;/, $arg, 4;
588 14 50       47 return $self->SetError("Bad for loop, expecting: 'init ;; condition ;; expression' at $self->{Place}", 1)
589             unless(@e == 3);
590              
591 14 100       76 $line->{Init} = $e[0] if($e[0] =~ /\S/);
592              
593 14 50       103 $e[1] = '1' unless($e[1] =~ /\S/); # Set default condition - true
594 14         29 $cond = $e[1];
595              
596 14 100       50 if($e[2] =~ /\S/) {
597             return $self->SetError("$dir for expression '$e[2]' fails to compile at $self->{Place}", 1)
598 11 50       49 unless($line->{For3} = $self->{Math}->Parse($e[2]));
599             }
600             }
601              
602             # Compile the condition below:
603 41         2889 $cond =~ s/^\s*//;
604 41         96 $arg = $cond;
605             }
606 347 100       665 if(exists $makeExpr{$dir}) {
607             # Precompile expression unless it is a .sub (starts '.'):
608 89 100       305 if(substr($arg, 0, length($self->{DirStart})) eq $self->{DirStart}) {
609 43         98 $line->{SubCond} = $arg;
610             } else {
611             return $self->SetError("$dir condition/expression fails to compile '$arg' at $self->{Place}", 1)
612 46 50 33     277 unless($arg =~ /\S/ and ($line->{Expr} = $self->{Math}->Parse($arg)));
613             }
614             }
615              
616 347 100 66     17012 if(defined($blkPairs{$dir})) {
    100          
    50          
617             # Start of block
618 100         162 push @blocks, {type => $dir, LoopStart => @{$code->{Lines}}+0 };
  100         425  
619             } elsif(defined($blkPairs{$blocks[-1]->{type}}) and $blkPairs{$blocks[-1]->{type}} eq $dir) {
620             # End of block
621              
622 100         186 my $blk = pop @blocks;
623              
624             # Consistency check
625             return $self->SetError("$leadin$dir followed by '$1' but match is '$blk->{type}' at $self->{Place}", 1)
626 100 50 66     251 if($arg =~ /(\S+)/ and $blk->{type} ne $1);
627              
628             # If loop add LoopStart/LoopEnd
629 100 100       327 if(exists $loops{$blk->{type}}) {
630 41         65 my $start = $blk->{LoopStart};
631 41         70 my $topl = $code->{Lines}->[$start];
632 41         85 $topl->{LoopStart} = $start;
633 41         62 $topl->{LoopEnd} = @{$code->{Lines}}+1;
  41         132  
634             }
635              
636             } elsif(defined($endings{$dir})) {
637 0         0 return $self->SetError("Unexpected $leadin$dir at $self->{Place} in $code->{Block} $started", 1)
638             }
639              
640             # Buffer in array
641 347         507 push @{$code->{Lines}}, $line;
  347         710  
642 347         522 $lineCnt++;
643              
644 347 100 100     987 last if($dir eq 'done' and @blocks == 0);
645             }
646              
647             # Next line
648             do {
649 0         0 return $self->SetError("Unexpected EOF at $self->{Place} while reading $code->{Block} $started", 1)
650 511 50       9170 } unless($InLine = (ref $self->{Frame}->{Fd} eq 'ARRAY') ? shift @{$self->{Frame}->{Fd}} : $self->{Frame}->{Fd}->getline);
  0 50       0  
651              
652 511         11391 $self->{Place} = "line $. of $self->{Frame}->{Name}";
653             }
654              
655 55         288 $code->{LastLine} = $code->{FirstLine} + $lineCnt - 1;
656             }
657              
658             # Run a sub: open a frame, process arguments
659             sub RunSub
660             {
661 165     165 0 428 my ($self, $dir, $arg) = @_;
662 165         436 my @args = $self->SplitArgs($arg, 1);
663 165         401 my $code = $self->{subs}->{$dir}; # Code read earlier
664              
665             # New frame to run the sub
666             $self->openFrame(Code => $code, Block => $dir, Args => [@args],
667             LineNumber => $code->{FileLine}, Name => $code->{FileName},
668 165         755 CodeLine => $code->{FirstLine}, ReturnFrom => 1);
669 165         326 my $frame = $self->{Frame};
670 165         365 delete $frame->{Fd};
671              
672             # If argument names are supplied, set as local vars:
673 165 100 66     485 if($code->{ArgNames} && @{$code->{ArgNames}}) {
  112         387  
674 112         223 my $vh = $self->{Math}->{VarHash};
675 112         183 foreach my $vname (@{$code->{ArgNames}}) {
  112         315  
676 228         417 my $vval = $vh->{$vname};
677 228         432 $frame->{LocalVars}->{$vname} = $vval;
678 228         345 delete($vh->{$vname});
679              
680 228 100       923 $vh->{$vname} = [shift @args] if(@args);
681             }
682             }
683             }
684              
685              
686             # Evaluate the condition, return true/false, or undef on error
687             # This could be a Math expression or a .sub returned value
688             # BEWARE: This could open a new frame to set up a sub, return to run it and frame.CondReReun
689             # will make the .if/... return here to see what the .return was.
690             sub EvalCond
691             {
692 479     479 0 1200 my ($self, $replay, $dir, $place, $arg) = @_;
693 479         727 my ($iftree, $true, $esc);
694              
695             # Is the condition a sub-call/directive ?
696 479 100 66     2288 if(($esc = exists($replay->{SubCond})) or
      100        
697             (substr($arg, 0, length($self->{DirStart})) eq $self->{DirStart} and $arg =~ /^$self->{DirStartRE}(\w+)\s*(.*)/)) {
698              
699             # If buffered code (loop/sub) get the arg string and break to subroutine and its arguments:
700 286 100       1587 ($arg = $replay->{SubCond}) =~ /^$self->{DirStartRE}(\w+)\s*(.*)/ if($esc);
701              
702 286         944 my ($sub, $args) = ($1, $2);
703              
704 286         444 my $intDir = 0; # If true: $sub is allowed internal directive
705 286 100       766 unless( exists $self->{subs}->{$sub}) {
706             return $self->SetError("Unknown sub '$sub' in $dir at $place", 1)
707 70 50       165 unless exists($condDirectives{$sub});
708 70         117 $intDir = 1;
709             }
710              
711 286 100       657 unless($self->{Frame}->{CondReRun}) {
712             # First time through:
713             # Set up the sub, return to main loop to run it
714 143         318 $self->{Frame}->{CondReRun} = 10;
715              
716             # Cause the .if/.while/... to be run again.
717             # If buffered back up a line, else push back to input for this frame
718 143 100       282 if($esc) {
719 122         227 $self->{Frame}->{CodeLine}--;
720             } else {
721 21         34 push @{$self->{Frame}->{PushedInput}}, "$self->{DirStart}$dir $arg";
  21         94  
722             }
723              
724 143 100       284 if($intDir) {
725             # Create a frame with just 1 line to run internal command
726 35         255 $self->openFrame(CodeLine => 0, Code => {Lines => [{ Txt => $arg, '#' => 1 }], LastLine => 0 } );
727             } else {
728             # Run the sub
729 108         295 $self->RunSub($sub, $args);
730             }
731              
732 143         342 $self->{Frame}->{CondReRun} = 1; # Cause return here
733 143         286 $self->{Frame}->{intDir} = $intDir; # Directive or sub ?
734 143         261 $self->{Frame}->{Generate} = 1; # Cause sub/directive to run
735              
736 143         374 return 0;
737              
738             } else {
739             # 2nd time:
740             # Get the command 'exit' code & tidy up:
741              
742 143 100       326 delete $self->{Frame}->{CondReRun} unless($esc);
743 143         331 $true = $self->{Math}->{VarHash}->{_}->[-1];
744             $self->closeFrame # Close internal command frame
745 143 50       382 if($self->{Frame}->{intDir});
746              
747 143 100       434 delete $self->{Frame}->{CondReRun} if($esc);
748             }
749              
750             } else {
751             # It is a conventional expression
752 193 100 66     712 if($replay and exists $replay->{Expr}) {
753 158         247 $iftree = $replay->{Expr};
754             } else {
755             return $self->SetError("Bad $self->{DirStart}$dir expression $place in $self->{Frame}->{Name} '$arg'", 1)
756 35 50       135 unless($iftree = $self->{Math}->Parse($arg));
757             }
758              
759 193         7375 $true = $self->{Math}->EvalToScalar($iftree);
760             }
761              
762 336 100       18456 $true = ! $true if($replay->{Not});
763              
764 336         825 $true
765             }
766              
767              
768              
769             # Return true on error
770             sub error
771             {
772 465     465 1 664 my $self = shift;
773              
774             $self->{Error} or (defined($self->{Frame}) and $self->{Frame}->{Fd} and
775             (ref $self->{Frame}->{Fd} ne 'ARRAY') and $self->{Frame}->{Fd}->error)
776 465 50 66     3488 }
      100        
      66        
777              
778             # As IO::Handle, clear recent error
779             sub clearerr
780             {
781 0     0 1 0 my $self = shift;
782              
783 0         0 $self->{Error} = 0;
784 0 0       0 return 0 if(ref $self->{Frame}->{Fd} eq 'ARRAY');
785 0 0 0     0 return -1 unless $self->{Fd} && $self->{Fd}->opened;
786              
787             $self->{Fd}->clearerr
788 0         0 }
789              
790             # Record the error at $errstr and maybe $!, note that there has been an error, return undef
791             # Arg is description of the error
792             # Optional extra arg. If true set $! to EINVAL - use this eg where file format error
793             sub SetError
794             {
795 0     0 0 0 my ($self, $errm, $einval) = @_;
796              
797 0         0 $self->{Error} = 1;
798 0         0 $errstr = $errm;
799              
800 0 0       0 die "$errm\n" if($self->{OnError} eq 'die' );
801 0 0       0 warn "$errm\n" if($self->{OnError} eq 'warn');
802              
803 0 0       0 if($einval) {
804 1     1   451 use Errno;
  1         1766  
  1         5831  
805 0         0 $! = &Errno::EINVAL;
806             }
807              
808             return undef
809 0         0 }
810              
811             # Put line(s) to be read as input
812             sub putline {
813 0     0 1 0 my $self = shift;
814              
815 0         0 push @{$self->{Frame}->{PushedInput}}, @_
  0         0  
816             }
817              
818             # Wrapper for _getline()
819             # The point is that we might be outputting to a stream rather than returning
820             # a line of input to the program.
821             sub getline {
822 1025     1025 1 14500 my $self = shift;
823              
824 1025         1565 while(1) {
825 1029         2181 my $l = $self->_getline;
826              
827 1029 100       4729 return $l if($self->{out} eq ""); # No stream set, return to caller
828              
829 4         12 $self->writeToStream($self->{out}, $l);
830             }
831             }
832              
833             # Write the argument line $l to the output $stream
834             # It can be an IO::FILE, array or subroutine
835             sub writeToStream {
836 9     9 0 19 my ($self, $stream, $l) = @_;
837              
838 9         19 my $strm = $self->{OutStreams}->{$stream};
839              
840 9 50       21 if( !defined($strm)) {
841             # This could result in many messages
842 0         0 $self->SetError("Output stream unknown: '$stream'");
843 0         0 return;
844             }
845 9 100 66     41 if(ref $strm eq 'IO::File' or ref $strm eq 'IO::Handle') {
846 4         43 print $strm $l;
847 4         11 return;
848             }
849 5 100       16 if(ref $strm eq 'CODE') {
850 1         7 &$strm($l);
851 1         7 return;
852             }
853 4 50       9 if(ref $strm eq 'ARRAY') {
854 4         8 push @$strm, $l;
855 4         8 return;
856             }
857             # Should not get here
858 0         0 $self->SetError("At $self->{Place}: Output stream '$stream' is not an IO::File or IO::Handle or array or subroutine, but is: " . ref $strm);
859             }
860              
861             # Called when every line is read
862             # One problem with this is that it cannot store anything in a local variable
863             # as it returns once it finds a line that it cannot process itself.
864             # Store in the object.
865             # Can't store 'static' since there may be different files open for different purposes.
866             # getline() getlines() close() are deliberately compatible with IO::Handle. new() is not, it is more complicated.
867             sub _getline {
868 1029     1029   1538 my $self = shift;
869              
870             return $self->SetError("A file has not been opened at $self->{Place}", 1)
871 1029 50 66     3423 unless defined $self->{Frame}->{Code} or defined $self->{Frame}->{Fd};
872              
873 1029         1373 my $doneDone = 0; # Last directive was .done
874 1029         1818 my $vh = $self->{Math}->{VarHash};
875              
876 1029         1252 while(1) {
877              
878             return undef
879 3088 50       6317 if $self->{Error};
880              
881             return undef
882 3088 100       3793 unless(@{$self->{FrameStk}});
  3088         6792  
883              
884 3027         4281 my $lineno;
885 3027         4081 my $frame = $self->{Frame};
886 3027         3766 my $replay;
887              
888 3027 100       3750 if(defined ($_ = pop @{$frame->{PushedInput}})) {
  3027 100       8558  
889             # Line pushed back to input
890 21         41 $lineno = $frame->{LineNumber};
891             } elsif(exists $frame->{Code}) {
892             # Loop or sub
893             # End of code ?
894 1717 100       3862 if($frame->{CodeLine} > $frame->{Code}->{LastLine}) {
895 85         248 $self->closeFrame;
896 85         478 next;
897             }
898              
899 1632         3066 $replay = $frame->{Code}{Lines}->[$frame->{CodeLine}++];
900 1632         2607 $_ = $replay->{Txt};
901 1632         2297 $lineno = $replay->{'#'};
902             } else {
903             # From file or in-memory array
904 1289 100       29176 $_ = (ref $frame->{Fd} ne 'ARRAY') ? $frame->{Fd}->getline : shift @{$frame->{Fd}};
  3         6  
905              
906             # EOF:
907 1289 100       35320 unless($_) {
908              
909             # EOF. Close the file. This may pop another one if there are multiple open files (.include)
910             return undef
911 81 50       302 unless($self->closeFrame);
912              
913             next # There is still a frame to look at
914 81 50       1507 if($self->{Frame});
915              
916             # EOF. Return undef
917 0         0 return undef;
918             }
919              
920 1208 50       2643 if($self->{Raw}) {
921 0         0 $vh->{_CountGen}->[0]++;
922 0         0 return $_;
923             }
924              
925 1208         2145 $lineno = $.;
926 1208         1984 chomp;
927             }
928              
929             # Store the line number in a silly number of places:
930 2861         4460 $frame->{LineNumber} = $lineno;
931 2861         7036 $vh->{'_LineNumber'} = [$lineno]; # do directly for speed
932 2861         8731 $self->{Math}->Eval($self->{SetLineMath});
933              
934             # Something that knows where the current line is:
935 2861         366365 my $place = "line $lineno of $frame->{Name}";
936 2861         5028 $self->{Place} = $place;
937              
938             EVAL_RESTART: # Restart parsing here after a .eval
939              
940             # Ignore comments
941 2905 100       10691 if(/^$self->{DirStartRE}#/) {
942 539 50       1284 warn "$place: $_\n" if($self->{trace});
943 539         1193 next;
944             }
945              
946 2366 50       13868 s/\s*$// if($self->{Trim});
947              
948             # Quick return if it cannot be a directive line - or one that we recognise
949             # If not generating - skip to next
950 2366 100 100     15004 unless(/^($self->{DirStartRE})(\w+)\s*(.*)/ and
      100        
951             (defined($ctlDirectives{$2}) or defined($self->{subs}->{$2}))) {
952 628 100       1774 unless($frame->{Generate}) {
953 61         125 $vh->{_CountSkip}->[0]++;
954 61         140 next;
955             }
956              
957 567 50       1208 warn "$place: $_\n" if($self->{trace} > 1);
958              
959 567         1093 $vh->{_CountGen}->[0]++;
960 567         2137 return $_ . $/; # Put the line terminator back on
961             }
962              
963             # Must be a directive:
964 1738         3478 my $leadin = $1; # String that identified the directive
965 1738         2650 my $dir = $2; # Directive
966 1738         2859 my $arg = $3; # Its argument
967              
968 1738 0 33     3907 warn "$place: $_\n" if($self->{trace} and $frame->{Generate});
969              
970 1738         3201 $vh->{_CountDirect}->[0]++;
971              
972             # Process .if/.else/.fi .unless
973             # Because we can have nested if/... we need a stack of how the conditions evaluated
974 1738 100 100     6002 if($dir eq 'if' or $dir eq 'unless') {
975             # start a new frame with .if
976             # Unless we are here a 2nd time as evaluating: .if .subroutine; in which case the frame is already open
977 104 100       521 $self->openFrame( Type => $dir, Else => 0, CpMove => 1) unless($frame->{CondReRun});
978 104         200 $frame = $self->{Frame};
979              
980 104         274 $frame->{ParentGenerate} = $frame->{DidGenerate} = $frame->{Generate};
981              
982             # Don't evaluate the .if if we are not generating, the expression could have side effects
983             # Don't compile it either - faster; but means that we only see errors if we try
984 104 50       233 if($frame->{Generate}) {
985 104         326 $replay->{Not} = $dir eq 'unless';
986              
987 104         337 my $gen = $self->EvalCond($replay, $dir, $place, $arg);
988 104 50       272 return $gen unless defined $gen;
989 104         199 $frame = $self->{Frame};
990 104 100       263 next if($frame->{CondReRun});
991 79         173 $frame->{DidGenerate} = $frame->{Generate} = $gen;
992             }
993              
994 79         221 next;
995             }
996 1634 100 100     4898 if($dir eq 'elseif' or $dir eq 'elsif') {
997             return $self->SetError("${leadin}$dir but an ${leadin}if/${leadin}unless has not been seen, at $place", 1)
998 29 50 66     100 unless($frame->{Type} eq 'if' or $frame->{Type} eq 'unless');
999             return $self->SetError("Cannot have ${leadin}$dir at $place to ${leadin}if after ${leadin}else at line $frame->{Else}", 1)
1000 29 50       63 if($frame->{Else});
1001              
1002             # Don't record that we have seen it, related errors always refer to the .if
1003              
1004             # We do the test only if the .if was false - exactly the same as .else below
1005             # Do a test if the .if was false and all parents (enclosing .ifs) are true, set the truth of Generate property.
1006              
1007 29 100 66     106 if($frame->{ParentGenerate} and !$frame->{DidGenerate}) {
1008 27         75 my $gen = $self->EvalCond($replay, $dir, $place, $arg);
1009 27         54 $frame = $self->{Frame};
1010 27 50       57 return $gen unless defined $gen;
1011 27         40 $frame = $self->{Frame};
1012              
1013 27 100       80 next if($frame->{CondReRun});
1014 15         82 $frame->{DidGenerate} = $frame->{Generate} = $gen;
1015             } else {
1016 2         5 $frame->{Generate} = 0; # Which it might already be
1017             }
1018              
1019 17         43 next;
1020             }
1021 1605 100       3337 if($dir eq 'else') {
1022              
1023             return $self->SetError("${leadin}else but an ${leadin}if has not been seen, at $place", 1)
1024 43 50 66     218 unless($frame->{Type} eq 'if' or $frame->{Type} eq 'unless');
1025             return $self->SetError("Another ${leadin}else at $place to ${leadin}if starting line $frame->{FrameStart}, first .else at line $frame->{Else}", 1)
1026 43 50       126 if($frame->{Else});
1027              
1028 43         99 $frame->{Else} = $lineno; # Note where the .else was
1029              
1030 43 100       113 if($frame->{DidGenerate}) {
1031 27         60 $frame->{Generate} = 0;
1032             } else {
1033 16         34 $frame->{Generate} = $frame->{ParentGenerate};
1034             }
1035              
1036 43         105 next;
1037             }
1038 1562 100       2628 if($dir eq 'fi') {
1039             return $self->SetError("${leadin}fi but an ${leadin}if has not been seen, $place", 1)
1040 75 50 66     288 unless($frame->{Type} eq 'if' or $frame->{Type} eq 'unless');
1041              
1042 75         237 $self->closeFrame;
1043 75         395 next;
1044             }
1045              
1046             # None of the rest unless generating:
1047 1487 100       3011 next unless $frame->{Generate};
1048              
1049 1461 100       2566 if($dir eq 'let') {
1050 151         234 my $iftree;
1051 151 100 66     572 if($replay and exists $replay->{Expr}) {
1052 87         159 $iftree = $replay->{Expr};
1053             } else {
1054             return $self->SetError("Bad ${leadin}let expression $place '$arg'", 1)
1055 64 50       239 unless($iftree = $self->{Math}->Parse($arg));
1056             }
1057 151         35330 $self->{Math}->EvalToScalar($iftree);
1058             # Don't care what the result is
1059              
1060 151         17658 next;
1061             }
1062              
1063             # Return a line parsed for escapes
1064 1310 100       2253 if($dir eq 'echo') {
1065 401         723 $vh->{_CountGen}->[0]++;
1066 401         996 return $self->ProcEscapes($arg) . $/;
1067             }
1068              
1069             # Start of loop
1070 909 100       1974 if(exists $loops{$dir}) { # 'while' 'until' 'for'
1071             # Create a new frame with an indicator that this is a loop frame
1072             # With 'for' execute the initialisation expression and record the loop expression
1073             # For/while/until all look the same (until has a truth invert flag)
1074             # On 'EOF' of the recorded array, detect that it is a loop frame:
1075             # - execute any loop expression
1076             # - evaluate the loop condition; closeFrame on false; reset CodeLine on true
1077              
1078 348         541 my $code;
1079 348         956 my @args = $self->SplitArgs($arg, 1);
1080 348         569 my $oframe = $frame;
1081              
1082             # First time:
1083 348 100 100     1081 unless($doneDone or $frame->{CondReRun}) {
1084 90         273 $self->openFrame(Block => $dir);
1085 90         178 $frame = $self->{Frame};
1086             }
1087              
1088             # If reading from a stream grab the loop to an array:
1089 348 100       802 unless(exists $frame->{Code}) {
1090 17 50       76 return $code unless($code = $self->ReadLoop($dir, $_, $arg));
1091 17         48 $frame->{Code} = $code;
1092 17         43 $frame->{CodeLine} = $code->{FirstLine} + 1;
1093 17         38 delete $frame->{Fd};
1094             }
1095              
1096             # New loop, initialise it:
1097 348 100 100     962 unless($doneDone or $frame->{CondReRun}) {
1098 90         250 $replay = $frame->{Code}{Lines}->[$frame->{CodeLine} - 1];
1099              
1100 90         207 $frame->{LoopMax} = $replay->{LoopMax};
1101 90         162 $frame->{LoopCnt} = 0;
1102 90         189 $frame->{LoopStart} = $replay->{LoopStart};
1103 90         147 $frame->{LoopEnd} = $replay->{LoopEnd};
1104              
1105             # Set CodeLine to Line after end - in parent frame (which might be from stream and ignore it)
1106 90         145 $oframe->{CodeLine} = $frame->{LoopEnd};
1107              
1108             # Evaluate any loop initialisation
1109 90 100       227 $self->{Math}->ParseToScalar($replay->{Init}) if(exists $replay->{Init});
1110             }
1111 348         5500 $doneDone = 0;
1112              
1113             # Beware: might be here twice
1114 348 100       718 unless($frame->{CondReRun}) {
1115             # Trap run away loops:
1116             return $self->SetError("Maximum iterations ($frame->{LoopMax}) exceeded at $frame->{FrameStart}", 1)
1117 242 50 66     903 if($frame->{LoopMax} && ++$frame->{LoopCnt} > $frame->{LoopMax});
1118              
1119             # evaluation loop expression (not on first time)
1120 242 100 100     851 $self->{Math}->EvalToScalar($replay->{For3}) if(exists $replay->{For3} and $frame->{LoopCnt} != 1);
1121             }
1122              
1123             # Evaluate the loop condition - if true keep looping
1124 348         5636 my $bool = $self->EvalCond($replay, $dir, $place, $arg);
1125 348 100       974 next if($frame->{CondReRun});
1126 242 100       603 $self->closeFrame if( !$bool);
1127              
1128 242         943 next;
1129             }
1130              
1131             # Should only be seen at end of loop - which is buffered
1132 561 100       1107 if($dir eq 'done') {
1133             return $self->SetError("Unexpected '$leadin$dir' at $place", 1)
1134 122 50       275 unless(exists $frame->{LoopMax});
1135              
1136             # Next to run is loop start:
1137 122         196 $frame->{CodeLine} = $frame->{LoopStart};
1138 122         185 $doneDone = 1;
1139 122         280 next;
1140             }
1141              
1142 439 100 66     1373 if($dir eq 'break' or $dir eq 'last') {
1143 27         55 my $loops = 1;
1144 27 100       74 $loops = $1 if($arg =~ /\s*(\d+)/);
1145              
1146             # Unwind until we find a LoopEnd, then close that frame
1147 27         37 my $le;
1148 27   66     44 do {
1149             # Can't break out of sub:
1150             return $self->SetError("'$leadin$dir' too many loops at $place", 1)
1151 28 50       103 if(exists $self->{Frame}->{ReturnFrom});
1152              
1153 28         56 $le = exists $self->{Frame}->{LoopEnd};
1154             return undef
1155 28 50       82 unless($self->closeFrame);
1156             } until($le and --$loops <= 0);
1157 27         148 next;
1158             }
1159              
1160 412 100 66     1328 if($dir eq 'continue' or $dir eq 'next') {
1161 30         44 my $loops = 1;
1162 30 100       76 $loops = $1 if($arg =~ /\s*(\d+)/);
1163              
1164             # Unwind until we find LoopStart, reset to that
1165 30         45 my $ls;
1166 30         52 while(1) {
1167             return $self->SetError("'$leadin$dir' too many loops at $place", 1)
1168 40 50       94 if(exists $self->{Frame}->{ReturnFrom});
1169              
1170 40 100 66     177 if(($ls = exists $self->{Frame}->{LoopStart}) && --$loops <= 0) {
1171 30         54 $self->{Frame}->{CodeLine} = $self->{Frame}->{LoopStart};
1172 30         57 last;
1173             }
1174              
1175             return undef
1176 10 50       27 unless($self->closeFrame);
1177             };
1178              
1179 30         42 $doneDone = 1; # This is like .done
1180 30         85 next;
1181             }
1182              
1183             # Local variable
1184 382 100       738 if($dir eq 'local') {
1185             # Push to previous var hash for this stack frame
1186             # This will be undone by closeFrame()
1187 16         75 foreach my $vname (split ' ', $arg) {
1188 28         48 my $vval = $vh->{$vname};
1189 28         57 $frame->{LocalVars}->{$vname} = $vval;
1190 28         59 delete($vh->{$vname});
1191             }
1192 16         36 next;
1193             }
1194              
1195             # Include another file
1196 366 100       751 if($dir eq 'include') {
1197 43         101 my (@push, @args, $stream, $fd);
1198 43         86 my $level = 0;
1199              
1200 43 100       211 if($arg =~ s/^-s\s*(\w+)\s*//i) {
1201 8         22 $stream = $1;
1202             return $self->SetError("Stream '$stream' already open at $place")
1203 8 50       31 if(exists($self->{Streams}->{$stream}));
1204             }
1205              
1206 43 50       162 return undef unless(@args = $self->SplitArgs($arg, 1));
1207 43 50       121 return $self->SetError("Missing include file at $place") unless(@args);
1208 43         90 my $fn = shift @args;
1209              
1210             # Push the include ?
1211 43 100 100     324 if(!defined($stream) and $fn =~ /^-p(\d*)$/) {
1212 2 100       9 $level = $1 eq '' ? 1 : $1; # Default 1
1213              
1214 0         0 return $self->SetError("Attempt to push too far (" . (scalar @{$self->{FrameStk}}) . " available) at $place")
1215 2 50       6 if($level > @{$self->{FrameStk}});
  2         9  
1216 2 50       8 return $self->SetError("Missing include file at $place") unless(@args);
1217 2         6 $fn = shift @args;
1218             }
1219              
1220             # Opening a pipe to read from ?
1221 43 100       212 if(substr($fn, 0, 1) eq '|') {
    100          
1222             return $self->SetError("Not allowed to open pipe at $place")
1223 12 50       55 unless($self->{PipeOK});
1224              
1225             # Replace the command if written '|cmd'
1226 12 100       46 $fn = $fn eq '|' ? shift(@args) : substr($fn, 1);
1227              
1228 12         141 $fd = IO::Pipe->new;
1229 12 50       1513 return $self->SetError("Open of pipe '$fn' failed: $! at $place")
1230             unless($fd);
1231              
1232 12         62 $fd->reader($fn, @args);
1233              
1234 12         11997 $fn = "| $fn"; # For messages, etc, only
1235              
1236 12         82 $vh->{_CountOpen}->[0]++;
1237             } elsif($fn =~ /^(@\w+)$/i) {
1238             return $self->SetError("Unknown in-memory stream '$1' at $place")
1239 1 50       5 unless(defined($self->{OutStreams}->{$1}));
1240 1         3 $fd = $self->{OutStreams}->{$1};
1241             } else {
1242 30 50       115 return undef unless(defined($fn = $self->ResolveFilename($fn)));
1243 30 50       106 return $self->SetError("Cannot open file '$arg' at $place as $!")
1244             unless($fd = $self->openFile($fn));
1245             }
1246              
1247             # Either store on a named stream or push to a frame
1248 43 100       179 if(defined($stream)) {
1249 8         133 $self->{Streams}->{$stream} = $fd;
1250             } else {
1251 35         269 $self->openFrame(Name => $fn, Fd => $fd, Args => [@args], Generate => 1, LineNumber => 0, ReturnFrom => 1);
1252 35 100       123 delete $self->{Frame}->{Code} if( !$level); # Back to input from file unless pushed elsewhere
1253 35 100       102 if($level) {
1254             # Insert opened stream/frame down in the stackframes:
1255 2         16 my $str = pop @{$self->{FrameStk}};
  2         14  
1256 2         6 splice @{$self->{FrameStk}}, -$level, 0, $str;
  2         34  
1257             }
1258             }
1259              
1260 43         546 next;
1261             }
1262              
1263             # Kill the script with optional exit code
1264 323 50       644 if($dir eq 'exit') {
1265 0         0 my $code = 2;
1266 0 0       0 if($arg ne '') {
1267 0         0 $code = $self->{Math}->ParseToScalar($arg);
1268 0 0       0 unless($code =~ /^\d+$/) {
1269 0         0 print "Exit expression at $place was not numeric: $code\n";
1270 0         0 $code = 2;
1271             }
1272             }
1273 0         0 exit $code;
1274             }
1275              
1276             # Print a line, -e print to stderr, -o stream write to named stream
1277             # Line parsed for escapes
1278 323 100       715 if($dir eq 'print') {
1279 5         10 my $stream = 'STDOUT';
1280 5 50       14 $stream = 'STDERR' if($arg =~ s/^-e\b\s*//);
1281 5 50       27 $stream = $1 if($arg =~ s/^-o\s*(@?\w+)\b\s*//i);
1282              
1283 5 50       17 return undef unless(defined($arg = $self->ProcEscapes($arg)));
1284 5         21 $self->writeToStream($stream, "$arg\n");
1285 5         10 next;
1286             }
1287              
1288             # Set the current output stream
1289 318 100       663 if($dir eq 'out') {
1290 7 100       49 if($arg eq "") {
    100          
    50          
1291 3         8 $self->{out} = "";
1292             } elsif($arg =~ s/^(-c)?\s*(@\w+)\s*$//i) {
1293             # Create the memory stream if -c option
1294 2 50       11 $self->{OutStreams}->{$2} = [] if(defined $1);
1295              
1296             $self->SetError("Output in-memory stream unknown: '$2' at $place")
1297 2 50       9 unless(defined $self->{OutStreams}->{$2});
1298 2         6 $self->{out} = $2;
1299             } elsif($arg =~ s/^(\w+)\s*$//i) {
1300             $self->SetError("Unknown output stream '$1' at $place")
1301 2 50       8 unless(defined($self->{OutStreams}->{$1}));
1302 2         6 $self->{out} = $1;
1303             } else {
1304 0         0 return $self->SetError("Bad or missing stream name '$arg' at $place");
1305             }
1306 7         16 next;
1307             }
1308              
1309             # Close this file, return to the one that .included it - if any
1310             # This may result in EOF. Check at loop top
1311 311 100       622 if($dir eq 'return') {
1312             # Evaluate expression after .return - in context of the .sub
1313 130         253 my $ret = undef;
1314 130 100       817 $ret = $self->{Math}->ParseToScalar($arg) if($arg =~ /\S/);
1315 130         23512 $vh->{_} = [$ret];
1316             return undef
1317 130 50       382 unless($self->close);
1318              
1319 130         1007 next;
1320             }
1321              
1322             # Eval: rewrite the line and try again
1323 181 100       381 if($dir eq 'eval') {
1324 44 50       137 return undef unless($_ = $self->ProcEscapes($arg));
1325 44 50       224 next if(/^$self->{DirStartRE}#/);
1326 44         93 $place = "Evaled: $place";
1327 44         240 goto EVAL_RESTART;
1328             }
1329              
1330             # Close a named stream
1331 137 100       274 if($dir eq 'close') {
1332 8 50       82 return $self->SetError("Missing option '-n stream' to ${leadin}close at $place", 1)
1333             unless($arg =~ s/^-s\s*(\w+)\s*//i);
1334              
1335 8         24 my $stream = $1;
1336              
1337             return $self->SetError("Unknown input stream '$stream' in ${leadin}read at $place", 1)
1338 8 50       32 unless(exists($self->{Streams}->{$stream}));
1339              
1340 8         266 delete($self->{Streams}->{$stream}); # Close it
1341              
1342 8         33 next;
1343             }
1344              
1345             # Read next line into var
1346 129 100       269 if($dir eq 'read') {
1347 29         65 my ($stream, $fd, $mem);
1348              
1349 29 100       169 $stream = $1 if($arg =~ s/^-s\s*(\w+)\s+//i);
1350              
1351             # In-memory stream:
1352 29 100       87 if($arg =~ s/^(@\w+)\s+//i) {
1353             return $self->SetError("Unknown in-memory stream $1 at $place")
1354 1 50       6 unless(defined $self->{OutStreams}->{$1});
1355 1         3 $mem = $1;
1356             }
1357              
1358 29         129 my ($vname) = $arg =~ /^(\w+)/i;
1359 29 50       87 return $self->SetError("Missing argument to ${leadin}read at $place", 1) unless($vname);
1360              
1361             # Find stream or Fd on stack:
1362 29 100       63 if(defined($stream)) {
1363             return $self->SetError("Unknown input stream '$stream' in ${leadin}read at $place", 1)
1364 23 50       74 unless($fd = $self->{Streams}->{$stream});
1365             } else {
1366             # Find an open file
1367 6         12 my $f = $frame;
1368 6         10 my $i = @{$self->{FrameStk}} - 1;
  6         19  
1369 6         21 until(exists($f->{Fd})) {
1370 3         9 $f = $self->{FrameStk}->[--$i];
1371             }
1372 6         12 $fd = $f->{Fd};
1373             }
1374              
1375 29         43 my $eof = 1;
1376 29 100       720 if($_ = (defined $mem) ? shift @{$self->{OutStreams}->{$mem}} : $fd->getline) {
  1 100       5  
1377 22         5469 chomp;
1378 22         37 $eof = 0;
1379 22 50       168 s/\s*$// if($self->{Trim});
1380             } else {
1381 7         199 $_ = '';
1382             }
1383              
1384 29         132 $vh->{$vname} = [$_];
1385 29         109 $vh->{'_EOF'} = [0 + $eof];
1386 29         78 $vh->{'_'} = [1 - $eof];
1387 29         90 next;
1388             }
1389              
1390             # No operation
1391 100 100       200 next if($dir eq 'noop');
1392              
1393             # Subroutine definition
1394 99 100       247 if($dir eq 'sub') {
1395             return undef
1396 38 50       164 unless($self->readSub($dir, $_, $arg));
1397 38         117 next;
1398             }
1399              
1400 61 100       133 if($dir eq 'test') {
1401 4         19 my %an = ('-f' => 2, '-m' => 2);
1402 4         16 my @args = $self->SplitArgs($arg, 1);
1403             return $self->SetError("'$leadin$dir' bad or missing argument '$arg' at $place", 1)
1404 4 50 33     37 unless(@args and exists($an{$args[0]}) and @args == $an{$args[0]});
      33        
1405              
1406 4 100       14 if($args[0] eq '-f') {
1407 2         5 my ($fn, @stat);
1408 2         7 $vh->{_} = [0]; # assume error
1409 2 50 33     11 if(($fn = $self->ResolveFilename($args[1], 1)) and (@stat = stat $fn)) {
1410 2         9 $vh->{_} = [1]; # OK
1411 2         6 $vh->{_STAT} = [@stat];
1412 2         7 $vh->{_TestFile} = [$fn];
1413             }
1414 2         8 next;
1415             }
1416              
1417 2 50       6 if($args[0] eq '-m') {
1418 2         7 $vh->{_} = [0]; # assume error
1419 2 100       6 if(defined($self->{OutStreams}->{$args[1]})) {
1420 1         4 $vh->{_} = [1]; # OK
1421 1         2 $vh->{_COUNT} = [scalar @{$self->{OutStreams}->{$args[1]}}];
  1         4  
1422             }
1423 2         7 next;
1424             }
1425             }
1426              
1427 57 50       133 if($dir eq 'error') {
1428 0 0       0 $arg = "Error at $place" if($arg eq '');
1429 0         0 return $self->SetError($arg);
1430             }
1431              
1432 57 50       110 if($dir eq 'set') {
1433             return $self->SetError("'$leadin$dir' bad argument '$arg' at $place")
1434 0 0 0     0 unless(($arg =~ /^(\w+)=(\d+)/i) and $options{$1});
1435 0         0 $self->{$1} = $2;
1436 0         0 next;
1437             }
1438              
1439 57 50 33     416 return $self->SetError("Use of reserved directive '$leadin$dir' at $place", 1)
      33        
      33        
      33        
1440             if($dir eq 'function' or $dir eq 'do' or $dir eq 'case' or $dir eq 'switch' or $dir eq 'endswitch');
1441              
1442             # User defined sub.
1443             # At the bottom so cannot redefine an inbuilt directive
1444 57 50       147 if(exists($self->{subs}->{$dir})) {
1445 57         210 $self->RunSub($dir, $arg);
1446              
1447 57         140 next;
1448             }
1449              
1450             # Should not happen
1451 0           return $self->SetError("Unknown directive '$leadin$dir' at $place", 1);
1452             }
1453             }
1454              
1455             # Return the rest of input as an array
1456             sub getlines
1457             {
1458 0     0 1   my $self = shift;
1459 0           my @lines = ();
1460              
1461             return $self->SetError("A file has not been opened", 1)
1462 0 0         unless $self->{Fd};
1463              
1464 0 0         return $self->SetError("getlines called in a scalar context", 1)
1465             unless(wantarray);
1466              
1467 0           while(my $line = $self->getline) {
1468 0           push @lines, $line;
1469             }
1470              
1471             @lines
1472 0           }
1473              
1474             # Enable the object to be used in the diamond operator:
1475 1     1   1442 use overload '<>' => \&getline, fallback => 1;
  1         1104  
  1         7  
1476              
1477             1;
1478              
1479             __END__