You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
2.7 KiB
111 lines
2.7 KiB
package ksb::RecursiveFH; |
|
|
|
use strict; |
|
use warnings; |
|
use v5.10; |
|
|
|
our $VERSION = '0.10'; |
|
|
|
use ksb::Util; |
|
|
|
# TODO: Replace make_exception with appropriate croak_* function. |
|
sub new |
|
{ |
|
my ($class) = @_; |
|
my $data = { |
|
'filehandles' => [], # Stack of filehandles to read |
|
'current' => undef, # Current filehandle to read |
|
}; |
|
|
|
return bless($data, $class); |
|
} |
|
|
|
sub addFilehandle |
|
{ |
|
my ($self, $fh) = @_; |
|
push @{$self->{filehandles}}, $fh; |
|
$self->setCurrentFilehandle($fh); |
|
} |
|
|
|
sub popFilehandle |
|
{ |
|
my $self = shift; |
|
my $result = pop @{$self->{filehandles}}; |
|
my $newFh = scalar @{$self->{filehandles}} ? ${$self->{filehandles}}[-1] |
|
: undef; |
|
$self->setCurrentFilehandle($newFh); |
|
return $result; |
|
} |
|
|
|
sub currentFilehandle |
|
{ |
|
my $self = shift; |
|
return $self->{current}; |
|
} |
|
|
|
sub setCurrentFilehandle |
|
{ |
|
my $self = shift; |
|
$self->{current} = shift; |
|
} |
|
|
|
# Reads the next line of input and returns it. |
|
# If a line of the form "include foo" is read, this function automatically |
|
# opens the given file and starts reading from it instead. The original |
|
# file is not read again until the entire included file has been read. This |
|
# works recursively as necessary. |
|
# |
|
# No further modification is performed to returned lines. |
|
# |
|
# undef is returned on end-of-file (but only of the initial filehandle, not |
|
# included files from there) |
|
sub readLine |
|
{ |
|
my $self = shift; |
|
|
|
# Starts a loop so we can use evil things like "redo" |
|
READLINE: { |
|
my $line; |
|
my $fh = $self->currentFilehandle(); |
|
|
|
# Sanity check since different methods might try to read same file reader |
|
return undef unless defined $fh; |
|
|
|
if (eof($fh) || !defined($line = <$fh>)) { |
|
my $oldFh = $self->popFilehandle(); |
|
close $oldFh; |
|
|
|
my $fh = $self->currentFilehandle(); |
|
|
|
return undef if !defined($fh); |
|
|
|
redo READLINE; |
|
} |
|
elsif ($line =~ /^\s*include\s+\S/) { |
|
# Include found, extract file name and open file. |
|
chomp $line; |
|
my ($filename) = ($line =~ /^\s*include\s+(.+)$/); |
|
|
|
if (!$filename) { |
|
die make_exception('Config', |
|
"Unable to handle file include on line $., '$line'"); |
|
} |
|
|
|
my $newFh; |
|
$filename =~ s/^~\//$ENV{HOME}\//; # Tilde-expand |
|
|
|
open ($newFh, '<', $filename) or |
|
die make_exception('Config', |
|
"Unable to open file $filename which was included from line $."); |
|
|
|
$self->addFilehandle($newFh); |
|
|
|
redo READLINE; |
|
} |
|
else { |
|
return $line; |
|
} |
|
} |
|
} |
|
|
|
1;
|
|
|