File Coverage

blib/lib/IO/ReadPreProcess.pm
Criterion Covered Total %
statement 543 602 90.2
branch 290 414 70.0
condition 92 141 65.2
subroutine 26 32 81.2
pod 10 24 41.6
total 961 1213 79.2


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