use strict;
#
# This program implements file inclusion. It obeys lines of the form
# include filename
# and otherwise copies input to output. It starts with a file name on the
# command line, if any, otherwise it uses standard input.
#
# File pointer. (Technically, a reference to a file pointer.)
my $fp;
# Initialize file pointer.
if(@ARGV) {
open($fp, $ARGV[0]) or die "Cannot open $ARGV[0]: $!\n";
} else {
$fp = \*STDIN;
}
# Stack of file handles indicating the the chain back to the starting file.
# Does not include the current input.
my @stack = ();
# Repeat until done. Notice that the outer while loop is labeled, and the
# the last statement refers to it. This allows the last to exit both
# looping levels.
outer: while(1) {
# Read a line. If that fails, pop a file descriptor and try again.
# If the pop fails, leave the outer loop.
my $l;
while(!($l = <$fp>)) {
close $fp;
$fp = pop @stack or last outer;
}
chomp $l;
# Check for the include input line which we obey.
if($l =~ /^\s*include\s+(.*?)\s*$/) {
my $fn = $1; # Get file name from pattern match.
my $newfp; # New descriptor.
if(open($newfp, $fn)) { # Open the new descriptor.
push @stack, $fp; # Stack the previous descriptor.
$fp = $newfp; # Switch to new one.
} else {
print "$fn: $!\n"; # Open failed. Print message.
}
next; # Succeed or fail, proceed to next line.
}
# Echo the input line.
print "$l\n";
}