head 1.2; access; symbols ePerl_2_2_14:1.1.1.1 RSE:1.1.1; locks; strict; comment @# @; 1.2 date 99.05.02.15.01.14; author rse; state Exp; branches; next 1.1; 1.1 date 99.05.02.14.43.39; author rse; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 99.05.02.14.43.39; author rse; state Exp; branches; next ; desc @@ 1.2 log @*** empty log message *** @ text @## ____ _ ## ___| _ \ ___ _ __| | ## / _ \ |_) / _ \ '__| | ## | __/ __/ __/ | | | ## \___|_| \___|_| |_| ## ## ePerl -- Embedded Perl 5 Language ## ## ePerl interprets an ASCII file bristled with Perl 5 program statements ## by evaluating the Perl 5 code while passing through the plain ASCII ## data. It can operate both as a standard Unix filter for general file ## generation tasks and as a powerful Webserver scripting language for ## dynamic HTML page programming. ## ## ====================================================================== ## ## Copyright (c) 1996,1997 Ralf S. Engelschall, All rights reserved. ## ## This program is free software; it may be redistributed and/or modified ## only under the terms of either the Artistic License or the GNU General ## Public License, which may be found in the ePerl source distribution. ## Look at the files ARTISTIC and COPYING or run ``eperl -l'' to receive ## a built-in copy of both license files. ## ## This program is distributed in the hope that it will be useful, but ## WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either the ## Artistic License or the GNU General Public License for more details. ## ## ====================================================================== ## ## ePerl.pm -- Perl interface to the ePerl parser (Perl part) ## package Parse::ePerl; # requirements and runtime behaviour require 5.00325; use strict; use vars qw($VERSION @@ISA @@EXPORT $AUTOLOAD); # imports require Exporter; require DynaLoader; require AutoLoader; use Carp; use Cwd qw(fastcwd); #use Safe; # interface @@ISA = qw(Exporter DynaLoader); @@EXPORT = qw(); # private version number $VERSION = do { my @@v=("2.3.0"=~/\d+/g); sprintf "%d."."%02d"x$#v,@@v }; # dynaloader bootstrapping bootstrap Parse::ePerl $VERSION; # untainting a variable: for restricted environments like # Apache/mod_perl under which our caller Apache::ePerl could run sub Untaint { my ($var) = @@_; # see perlsec(1) ${$var} =~ m|^(.*)$|s; ${$var} = $1; } ## ## Preprocess -- run the ePerl preprocessor over the script ## which expands #include directives ## sub Preprocess ($) { my ($p) = @@_; my ($result, $ocwd); # error if no input or no output if ( not $p->{Script} || not $p->{Result}) { return 0; } # set defaults $p->{INC} ||= [ '.' ]; # switch to directory of file if ($p->{Cwd}) { Untaint(\$p->{Cwd}); $ocwd = fastcwd(); chdir($p->{Cwd}); } # use XS part: PP (preprocessor) $result = PP( $p->{Script}, $p->{INC} ); # restore Cwd chdir($ocwd) if ($p->{Cwd}); if ($result eq '') { return 0; } else { ${$p->{Result}} = $result; return 1; } } ## ## Translate -- translate a plain Perl script from ## bristled code to plain Perl code ## sub Translate ($) { my ($p) = @@_; my ($result); # error if no input or no output if ( not $p->{Script} || not $p->{Result}) { return 0; } # set defaults $p->{BeginDelimiter} ||= '<:'; $p->{EndDelimiter} ||= ':>'; $p->{CaseDelimiters} ||= 0; $p->{ConvertEntities} ||= 0; # use XS part: Bristled2Plain $result = Bristled2Plain( $p->{Script}, $p->{BeginDelimiter}, $p->{EndDelimiter}, $p->{CaseDelimiters}, $p->{ConvertEntities} ); if ($result eq '') { return 0; } else { ${$p->{Result}} = $result; return 1; } } ## ## Precompile -- precompile a plain Perl script to ## internal Perl code (P-code) by storing ## the script into a subroutine ## sub Precompile ($) { my ($p) = @@_; my ($error, $func, $ocwd); # error if no input or no output if ( not $p->{Script} || not $p->{Result}) { return 0; } # capture the warning messages which # usually are send to STDERR and # disable the die of the interpreter $error = ''; local $SIG{'__WARN__'} = sub { $error .= $_[0]; }; local $SIG{'__DIE__'}; # switch to directory of file if ($p->{Cwd}) { Untaint(\$p->{Cwd}); $ocwd = fastcwd(); chdir($p->{Cwd}); } # precompile the source into P-code #my $cp = new Safe("Safe::ePerl"); #$func = $cp->reval('$func = sub {'.$p->{Script}.'};'); Untaint(\$p->{Script}); eval("\$func = sub {" . $p->{Script} . "};"); $error = "$@@" if ($@@); # restore Cwd chdir($ocwd) if ($p->{Cwd}); # return the result if ($error) { $error =~ s|\(eval \d+\)|$p->{Name}| if ($p->{Name}); ${$p->{Error}} = $error if ($p->{Error}); $@@ = $error; return 0; } else { ${$p->{Result}} = $func; $@@ = ''; return 1; } } ## ## Evaluate -- evaluate a script which is either ## give as a P-code reference or as ## a plain Perl script sub Evaluate ($) { my ($p) = @@_; my ($stdout, $stderr, %OENV, $ocwd); my ($result, $error); # error if no input or no output if ( not $p->{Script} || not $p->{Result}) { return 0; } # capture STDOUT and STDERR $stdout = tie(*STDOUT, 'Parse::ePerl'); $stderr = tie(*STDERR, 'Parse::ePerl'); # setup the environment if ($p->{ENV}) { %OENV = %ENV; %ENV = %{$p->{ENV}}; } # switch to directory of file if ($p->{Cwd}) { $ocwd = fastcwd(); chdir($p->{Cwd}); } # capture the warning messages which # usually are send to STDERR (and which # cannot be captured by our tie!) plus # disable the die of the interpreter $error = ''; local $SIG{'__WARN__'} = sub { $error .= $_[0]; }; local $SIG{'__DIE__'} = sub { $error .= $_[0]; }; # now evaluate the script which # produces content on STDOUT and perhaps # additionally on STDERR if (ref($p->{Script})) { # a P-code reference &{$p->{Script}}; } else { # a plain code string eval $p->{Script}; } # retrieve captured data from STDOUT $result = ${$stdout}; # retrieve either the error message # (on syntax errors) or the generated data # on STDERR (when generated by the script) $error ||= ${$stderr}; $error =~ s|\(eval \d+\)|$p->{Name}| if (defined($error) && $p->{Name}); # restore Cwd chdir($ocwd) if ($p->{Cwd}); # restore environment %ENV = %OENV if ($p->{ENV}); # remove capturing mode from STDOUT/STDERR undef($stdout); undef($stderr); untie(*STDOUT); untie(*STDERR); # set the result ${$p->{Result}} = $result; ${$p->{Error}} = $error if ($p->{Error}); # return the result codes if ($error) { $@@ = $error; return 0; } else { $@@ = ''; return 1; } } ## ## Expand -- the steps Translate & Evaluate ## just combined into one step ## sub Expand ($) { my ($p) = @@_; my ($rc, $script); # error if no input or no output if ( not $p->{Script} || not $p->{Result}) { return 0; } if (not Translate($p)) { return 0; } $script = $p->{Script}; $p->{Script} = ${$p->{Result}}; $rc = Evaluate($p); $p->{Script} = $script; return $rc; } ## ## Capture -- methods for capturing a filehandle ## (used by Evaluate) via this class ## sub TIEHANDLE { my ($class, $c) = @@_; return bless(\$c,$class); } sub PRINT { my ($self) = shift; ${$self} .= join('', @@_); } sub PRINTF { my ($self) = shift; my ($fmt) = shift; ${$self} .= sprintf($fmt, @@_); } # sometimes Perl wants it... sub DESTROY { }; 1; ##EOF## __END__ =head1 NAME Parse::ePerl - Perl interface to the ePerl parser =head1 SYNOPSIS use Parse::ePerl; $rc = Parse::ePerl::Preprocess($p); $rc = Parse::ePerl::Translate($p); $rc = Parse::ePerl::Precompile($p); $rc = Parse::ePerl::Evaluate($p); $rc = Parse::ePerl::Expand($p); =head1 DESCRIPTION Parse::ePerl is the Perl 5 interface package to the functionality of the ePerl parser (see eperl(1) for more details about the stand-alone program). It directly uses the parser code from ePerl to translate a bristled script into a plain Perl script and additionally provides functions to precompile such scripts into P-code and evaluate those scripts to a buffer. All functions are parameterized via a hash reference C<$p> which provide the necessary parameters. The result is a return code C<$rc> which indicates success (1) or failure (0). =head2 B This is the ePerl preprocessor which expands C<#include> directives. See eperl(1) for more details. Possible parameters for C<$p>: =over 4 =item I