package SGMLS::Output; use Carp; use Exporter; @ISA = Exporter; @EXPORT = qw(output push_output pop_output); $version = '$Id: Output.pm,v 1.6 1995/12/05 12:21:51 david Exp $'; =head1 NAME SGMLS::Output - Stack-based Output Procedures =head1 SYNOPSIS use SGMLS::Output; To print a string to the current output destination: output($data); To push a new output level to the filehandle DATA: push_output('handle',DATA); To push a new output level to the file "foo.data" (which will be opened and closed automatically): push_output('file','foo.data'); To push a new output level to a pipe to the shell command "sort": push_output('pipe','sort'); To push a new output level I to the file "foo.data": push_output('append','foo.data'); To push a new output level to an empty string: push_output('string'); To push a new output level appending to the string "David is ": push_output('string',"David is "); To push a new output level to The Great Beyond: push_output('nul'); To revert to the previous output level: pop_output(); To revert to the previous output level, returning the contents of an output string: $data = pop_output(); =head1 DESCRIPTION This library allows redirectable, stack-based output to files, pipes, handles, strings, or nul. It is especially useful for packages like L, since handlers for individual B elements can temporarily change and restore the default output destination. It is also particularly useful for capturing the contents of an element (and its sub-elements) in a string. Example: sgmls('', sub{ push_output('string'); }); sgmls('', sub{ $title = pop_output(); }); In between, anything sent to B (such as CDATA) will be accumulated in the string returned from B. Example: sgmls('', sub { push_output('nul'); }); sgmls('', sub { pop_output(); }); All output will be ignored until the header has finished. =head1 AUTHOR AND COPYRIGHT Copyright 1994 and 1995 by David Megginson, C. Distributed under the terms of the Gnu General Public License (version 2, 1991) -- see the file C which is included in the B distribution. =head1 SEE ALSO: L. =cut # # Anonymous subroutines for handling different types of references. # $output_handle_sub = sub { print $current_output_data @_; }; $output_file_sub = sub { print $current_output_data @_; }; $output_string_sub = sub { $current_output_data .= shift; foreach (@_) { $current_output_data .= $, . $_; } $current_output_data .= $\; }; $output_nul_sub = sub {}; # # Status variables # $current_output_type = 'handle'; $current_output_data = STDOUT; $current_output_sub = $output_handle_sub; @output_stack = qw(); # # Externally-visible functions # # Send data to the output. sub output { &{$current_output_sub}(@_); } # Push a new output destination. sub push_output { my ($type,$data) = @_; push @output_stack, [$current_output_type,$current_output_data, $current_output_sub]; SWITCH: { $type eq 'handle' && do { # Force unqualified filehandles into caller's package my ($package) = caller; $data =~ s/^[^':]+$/$package\:\:$&/; $current_output_sub = $output_handle_sub; $current_output_type = 'handle'; $current_output_data = $data; last SWITCH; }; $type eq 'file' && do { $current_output_sub = $output_file_sub; my $handle = new_handle(); open($handle,">$data") || croak "Cannot create file $data.\n"; $current_output_type = 'file'; $current_output_data = $handle; last SWITCH; }; $type eq 'pipe' && do { $current_output_sub = $output_file_sub; my $handle = new_handle(); open($handle,"|$data") || croak "Cannot open pipe to $data.\n"; $current_output_type = 'file'; $current_output_data = $handle; last SWITCH; }; $type eq 'append' && do { $current_output_sub = $output_file_sub; my $handle = new_handle(); open($handle,">>$data") || croak "Cannot append to file $data.\n"; $current_output_type = 'file'; $current_output_data = $handle; last SWITCH; }; $type eq 'string' && do { $current_output_sub = $output_string_sub; $current_output_type = 'string'; $current_output_data = $data; last SWITCH; }; $type eq 'nul' && do { $current_output_sub = $output_nul_sub; $current_output_type = 'nul'; $current_output_data = ''; last SWITCH; }; croak "Unknown output type: $type.\n"; } } # Pop the current output destination. sub pop_output { my ($old_type,$old_data) = ($current_output_type,$current_output_data); ($current_output_type,$current_output_data,$current_output_sub) = @{pop @output_stack}; SWITCH: { $old_type eq 'handle' && do { return $old_data; }; $old_type eq 'file' && do { close($old_data); return ''; }; $old_type eq 'string' && do { return $old_data; }; $old_type eq 'nul' && do { return ''; }; croak "Unknown output type: $type.\n"; } } # # Local Utility functions. # $new_handle_counter = 1; sub new_handle { return "IOHandle" . $new_handle_counter++; } 1;