#!/usr/bin/perl #------------------------------------------------------------------------------ # $Id: hdiff,v 1.7 2002/08/16 05:50:51 peters Exp $ # # NAME: hdiff # # PURPOSE: HTML diff. Produces a colourised HTML diff output which is # much easier to read than standard diff output. # # SOURCE: http://www.ginini.com/software/hdiff/ # # hdiff is derived from cvs2html from www.sslug.dk/cvs2html # #------------------------------------------------------------------------------ require 5.000; use Getopt::Std; # # You must use a version of diff that has unified output. GNU diff has this # option, so you can leave the default setting as is on Linux based systems. # For most other Unix systems, grab a copy of GNU diff from # ftp://ftp.gnu.org/gnu/diffutils # $diff = '/usr/bin/diff'; # # Colors and font to show the diff type of code changes # $diffcolorEmpty = '#CCCCCC'; # color of 'empty' lines $diffcolorAdd = '#FF9999'; # Removed line(s) (left) ( - ) $diffcolorChange = '#99FF99'; # Changed line(s) ( both ) $diffcolorRemove = '#CCCCFF'; # Added line(s) ( - ) (right) $diffcolorDarkChange = '#99CC99'; # lines, which are empty in change $diffcolorLineNum = '#666666'; # Lines containing line numbers #----------------------------------------------------------------------------- # END OF CONFIGURABLE OPTIONS #----------------------------------------------------------------------------- $Version = "2.1.0"; # # Process options # # Keep warnings happy use vars qw($opt_s $opt_r $opt_n $opt_N); getopts('h:f:t:rl:sL:c:C:e:nNo:') or Usage(); Usage() unless ( scalar @ARGV == 2 ); $File1 = $ARGV[0]; $File2 = $ARGV[1]; if ( defined $opt_o ) { open STDOUT, ">$opt_o" or die "Error creating file '$opt_o' ($!)"; } $tabstop = ( $opt_e > 0 ? $opt_e : 8 ) if defined $opt_e; if ($opt_t) { $Title = "$opt_t"; } else { $Title = "hdiff output"; } $opt_L = 'legend,Lines Added,Lines changed,Lines Removed,- No viewable Change -,Line ##NUM#' unless $opt_L; ( $TextLegend, $TextAdded, $TextChanged, $TextRemoved, $TextNoChange, $TextLineNumber ) = map htmlify($_), split /,/, $opt_L . ", MISSING , MISSING , MISSING , MISSING , MISSING , MISSING "; $LeftCaptionText = ( $opt_c ? htmlify($opt_c) : '#NAME#' ); $RightCaptionText = ( $opt_C ? htmlify($opt_C) : '#NAME#' ); die "$File1 not found\n" if ( ( !-f $File1 ) && ( !-d $File1 ) ); die "$File2 not found\n" if ( ( !-f $File2 ) && ( !-d $File2 ) ); HtmlHeader(); human_readable_diff( $File1, $File2 ); HtmlFooter(); #-------------------------------------------------------------------------- sub htmlify { my ( $string, $pr ) = @_; # Special Characters; RFC 1866 $string =~ s/&/&/g; $string =~ s/\"/"/g; $string =~ s//>/g; # get URL's as link .. $string =~ s§(http|ftp)(://[-a-zA-Z0-9%.~:/]+)([?&]([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*§$1$2$3§; # get e-mails as link $string =~ s§([-a-zA-Z0-9.]+@([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})§$1§; # get #PR as link .. if ( $pr && defined $prcgi ) { $string =~ s!\b((pr[:#]?\s*#?)|((bin|conf|docs|gnu|i386|kern|misc|ports)\/))(\d+)\b!$&!ig; } return $string; } #------------------------------------------------------------------------------ sub spacedHtmlText { my ( $string, $pr ) = @_; # Cut trailing spaces s/\s+$//; # Expand tabs if ( defined $tabstop ) { while ( $string =~ s/\t+/' ' x (length($&) * $tabstop - length($`) % $tabstop)/e ) { } } # replace and (§ is to protect us from htmlify) # gzip can make excellent use of this repeating pattern :-) $string =~ s/§/§%/g; #protect our & substitute $string =~ s/ /§nbsp;§nbsp;§nbsp;§nbsp;§nbsp;§nbsp;§nbsp;§nbsp;/g; $string =~ s/ /§nbsp;/g; $string = htmlify($string); # unescape $string =~ s/§([^%])/&$1/g; $string =~ s/§%/§/g; return $string; } #------------------------------------------------------------------------------ sub LineNumText { my ($LineNum) = @_; return '' unless $opt_n; return spacedHtmlText( sprintf( "%3d:", ($$LineNum)++ ) ); } #------------------------------------------------------------------------------ sub flush_diff_rows { my $j; my ( $leftColRef, $rightColRef, $leftRow, $rightRow ) = @_; if ( $state eq "PreChangeRemove" ) { # we just got remove-lines before for ( $j = 0 ; $j < $leftRow ; $j++ ) { print "@$leftColRef[$j]"; print " \n"; } } elsif ( $state eq "PreChange" ) { # state eq "PreChange" # we got removes with subsequent adds for ( $j = 0 ; $j < $leftRow || $j < $rightRow ; $j++ ) { # dump out both cols print ""; if ( $j < $leftRow ) { print "@$leftColRef[$j]"; } else { print " "; } if ( $j < $rightRow ) { print "@$rightColRef[$j]"; } else { print " "; } print "\n"; } } } #------------------------------------------------------------------------------ sub HtmlHeader { if ($opt_h) { my $header_content; open HEADER, $opt_h or die "Can not open $opt_h $!\n"; $header_content = join ( "",
); $header_content =~ s/#TITLE/$Title/g; print $header_content; close(HEADER); } else { print qq(\n\n); print qq($Title\n); print qq(\n); print qq(\n); print qq(

$Title

\n); } } #------------------------------------------------------------------------------ sub HtmlFooter { print < $TextLegend $TextAdded  $TextChanged  $TextRemoved

hdiff - version: $Version

EOF if ($opt_f) { open FOOTER, $opt_f or die "Can not open $opt_f $!\n"; print