package Apache::Syntax::Highlight::Perl; require 5.005; use strict; use vars qw($VERSION); $VERSION = '1.01'; use mod_perl; use constant MP2 => ($mod_perl::VERSION >= 1.99); use Syntax::Highlight::Perl; use IO::File; my $can_cache; my %stat; BEGIN { # Tests mod_perl version and uses the appropriate components if (MP2) { require Apache::Const; Apache::Const->import(-compile => qw(DECLINED OK)); require Apache::RequestRec; require Apache::RequestIO; require Apache::RequestUtil; } else { require Apache::Constants; Apache::Constants->import(qw(DECLINED OK)); } # Test caching necessaries modules eval { require Digest::MD5; Digest::MD5->can('md5_hex') }; $can_cache = $@ ? 0 : 1; } my %default_styles = ( 'Comment_Normal' => 'color:#006699;font-style:italic;', 'Comment_POD' => 'color:#001144;font-style:italic;', 'Directive' => 'color:#339999;font-style:italic;', 'Label' => 'color:#993399;font-style:italic;', 'Quote' => 'color:#0000aa;', 'String' => 'color:#0000aa;', 'Subroutine' => 'color:#998800;', 'Variable_Scalar' => 'color:#008800;', 'Variable_Array' => 'color:#ff7700;', 'Variable_Hash' => 'color:#8800ff;', 'Variable_Typeglob' => 'color:#ff0033;', 'Whitespace' => '', 'Character' => 'color:#880000;', 'Keyword' => 'color:#000000;', 'Builtin_Operator' => 'color:#330000;', 'Builtin_Function' => 'color:#000011;', 'Operator' => 'color:#000000;', 'Bareword' => 'color:#33AA33;', 'Package' => 'color:#990000;', 'Number' => 'color:#ff00ff;', 'Symbol' => 'color:#000000;', 'CodeTerm' => 'color:#000000;', 'DATA' => 'color:#000000;', 'LineNumber' => 'color:#CCCCCC;' ); sub handler { my $r = shift; my $str; # buffered output my $mtime; my $have_to_cache = 0; return (MP2 ? Apache::DECLINED : Apache::Constants::DECLINED) if $r->args =~ /download/i; my $sln = ($r->dir_config('HighlightShowLineNumbers') =~ /^on$/i || $r->args =~ /ShowLineNumbers/i) ? 1 : 0; my $key = $r->filename . $sln; my $debug = $r->dir_config('HighlightDebug') eq 'On' ? 1 : 0; # Cache feature if ( $can_cache && $r->dir_config('HighlightCache') =~ /^on$/i ) { $mtime = (stat $r->filename)[9]; # File needs to be processed if ( ! defined $stat{$key} || $mtime > $stat{$key} ) { $stat{$key} = $mtime; $have_to_cache = 1; print STDERR "[$$] We have to cache!\n" if $debug; } # We have already in cache else { $str = get_cache( file => $key, dir => $r->dir_config('HighlightCacheDir') || '/tmp', debug => $debug ); } use Data::Dumper; print STDERR ("[$$] " . $r->filename . "\n" . Dumper(\%stat)) if $debug; } # When we must highlight? if ( $have_to_cache || ! $str ) { print STDERR "[$$] Generating highlight...\n" if $debug; my $formatter = new Syntax::Highlight::Perl; # Open file to highlight my $fh = new IO::File($r->filename); # Escapes HTML $formatter->define_substitution('<' => '<', '>' => '>', '&' => '&'); # Install the formats if ( $r->dir_config('HighlightCSS') ) { foreach (keys %default_styles) { $formatter->set_format($_, [ "",'' ] ); } $str = '
';
		}
		else {
			while ( my($type,$style) = each %default_styles ) {
				$formatter->set_format($type, [ "",'' ] );
				$str = '
';
			}
		}
		my @lines = $formatter->format_string(<$fh>);
		undef $fh;

		# Adds line numbers
		if ( $sln ) {
			my $line_number = 1;
			my $max_space = length($formatter->line_count) + 1;
			@lines = map { ' ' x ($max_space - length($line_number)) . '' . $line_number++ . ' ' . $_ } @lines;
		}
		$str .= join('',@lines) . '
'; } if ( $have_to_cache ) { put_cache( file => $key, content => $str, dir => $r->dir_config('HighlightCacheDir') || '/tmp', debug => $debug ); } # Output code to client $r->content_type('text/html'); MP2 ? 1 : $r->send_http_header; $r->print($str); return MP2 ? Apache::OK : Apache::Constants::OK; } sub get_cache { my %args = @_; $args{'key'} ||= Digest::MD5->md5_hex($args{'file'}); return undef if ! $args{'file'}; print STDERR "[$$] Opening file: $args{'dir'}/$args{'key'}\n" if $args{'debug'}; my $fh = new IO::File("$args{'dir'}/$args{'key'}"); my $slurp = do { local $/; <$fh> }; return $slurp; } sub put_cache { my %args = @_; return 0 if ( $args{'dir'} !~ /^\/tmp/ ); $args{'key'} ||= Digest::MD5->md5_hex($args{'file'}); return 0 if ( ! $args{'key'} || ! $args{'content'} ); print STDERR "[$$] Writing file: $args{'dir'}/$args{'key'}\n" if $args{'debug'}; my $fh; if ( open($fh,">$args{'dir'}/$args{'key'}") ) { flock($fh,2) if $^O !~ /win32/i; print $fh $args{'content'}; flock($fh,8) if $^O !~ /win32/i; close($fh); return 1; } return 0; } 1; __END__ =pod =head1 NAME Apache::Syntax::Highlight::Perl - mod_perl 1.0/2.0 extension to highlight Perl code =head1 SYNOPSIS In F (mod_perl 1): PerlModule Apache::Syntax::Highlight::Perl SetHandler perl-script PerlHandler Apache::Syntax::Highlight::Perl PerlSetVar HighlightShowLineNumbers On PerlSetVar HighlightCSS http://path.to/highlight.css In F (mod_perl 2): PerlModule Apache2 PerlModule Apache::Syntax::Highlight::Perl SetHandler perl-script PerlResponseHandler Apache::Syntax::Highlight::Perl PerlSetVar HighlightShowLineNumbers On PerlSetVar HighlightCSS http://path.to/highlight.css =head1 DESCRIPTION Apache::Syntax::Highlight::Perl is a mod_perl (1.0 and 2.0) module that provides syntax highlighting for Perl code. This module is a wrapper around L. =head1 MOD_PERL 2 COMPATIBILITY Apache::Syntax::Highlight::Perl is fully compatible with both mod_perl generations 1.0 and 2.0. If you have mod_perl 1.0 and 2.0 installed on the same system and the two uses the same per libraries directory, to use mod_perl 2.0 version make sure to load first C module which will perform the necessary adjustements to C<@INC>: PerlModule Apache2 PerlModule Apache::Syntax::Highlight::Perl Of course, notice that if you use mod_perl 2.0, there is no need to pre-load the L compatibility layer. =head1 INSTALLATION In order to install and use this package you will need Perl version 5.005 or better. Prerequisites: =over 4 =item * mod_perl 1 or 2 (of course) =item * Syntax::Highlight::Perl >= 1.00 =back Installation as usual: % perl Makefile.PL % make % make test % su Password: ******* % make install =head1 CONFIGURATION In order to enable Perl file syntax highlighting you could modify I or I<.htaccess> files. =head1 DIRECTIVES You can control the behaviour of this module by configuring the following variables with C directive in the I (or I<.htaccess> files) =over 4 =item C string This single directive sets the URL (or URI) of the custom CCS file. PerlSetVar HighlightCSS /highlight/perl.css It can be placed in server config, , , , and F<.htaccess> context. The CSS file is used to define styles for all the syntactical elements that L currently recognizes. For each style there is a correspondant syntactical element. The elements are: =over 4 =item Comment_Normal Default is C<{color:#006699;font-style:italic;}> =item Comment_POD Default is C<{color:#001144;font-family:garamond,serif;font-size:11pt;font-style:italic;}> =item Directive Default is C<{color:#339999;font-style:italic;}> =item Label Default is C<{color:#993399;font-style:italic;}> =item Quote Default is C<{color:#0000aa;}> =item String Default is C<{color:#0000aa;}> =item Subroutine Default is C<{color:#998800;}> =item Variable_Scalar Default is C<{color:#008800;}> =item Variable_Array Default is C<{color:#ff7700;}> =item Variable_Hash Default is C<{color:#8800ff;}> =item Variable_Typeglob Default is C<{color:#ff0033;}> =item Whitespace Not yet used =item Character Default is C<{color:#880000;}> =item Keyword Default is C<{color:#000000; font-weight:bold;}> =item Builtin_Function Default is C<{color:#000000; font-weight:bold;}> =item Builtin_Operator Default is C<{color:#000000; font-weight:bold;}> =item Operator Default is C<{color:#000000;}> =item Bareword Default is C<{color:#33AA33;}> =item Package Default is C<{color:#990000;}> =item Number Default is C<{color:#ff00ff;}> =item Symbol Default is C<{color:#000000;}> =item CodeTerm Default is C<{color:#AA0000;}> =item DATA Default is C<{color:#CCCCCC;}> =item LineNumber This style hasn't a correspondant syntactical element but is used to display line numbers to the right of the code. Default is C<{color:#CCCCCC;}> =back See C section of L POD for more informations about elements currently recognized. =item C On|Off This single directive displays line numbers to the right of the text PerlSetVar HighlightShowLineNumbers On It can be placed in server config, , , , and F<.htaccess> context. The default value is C. =item C On|Off This directive enables a very simple cache layer of already and unchanged highlighted files: PerlSetVar HighlightCache On Default is C. =item C string This directive sets cache directory PerlSetVar HighlightCacheDir /tmp/highlight Default is C. =back =head1 RUN TIME CONFIGURATION In addition, you can control the module behaviour at run time by adding some values via the query string. In particular: =over 4 =item download Forces the module to exit with DECLINED status, for example by allowing users to download the file (according to Apache configuration): http://myhost.com/myproject/sample.pl?download =item showlinenumbers Forces showing of code line numbers. For example: http://myhost.com/myproject/sample.pl?showlinenumbers =back =head1 BUGS Please submit bugs to CPAN RT system at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Apache%3A%3ASyntax%3A%3AHighlight%3A%3APerl or by email at bug-apache-syntax-highlight-perl@rt.cpan.org Patches are welcome and I'll update the module if any problems will be found. =head1 VERSION Version 1.01 =head1 TODO =over 4 =item * Use of Cache::Cache:: family in order to cache highlighted files. back =head1 SEE ALSO L, L, L, perl =head1 AUTHOR Enrico Sorcinelli, Eenrico@sorcinelli.itE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004 by Enrico Sorcinelli This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.2 or, at your option, any later version of Perl 5 you may have available. =cut