hdiff output

example1-file1 Fri Aug 16 15:51:23 2002 example1-file2 Sun Jun 9 12:24:37 2002
 #!/usr/bin/perl #!/usr/bin/perl 
 #------------------------------------------------------------------------------ #------------------------------------------------------------------------------
 # $Id: hdiff,v 1.7 2002/08/16 05:50:51 peters Exp $ # $Id: hdiff,v 1.4 2002/06/09 02:18:18 peters Exp $
 # #
 # NAME:                hdiff # NAME:                hdiff
 # #
 # PURPOSE:        HTML diff. Produces a colourised HTML diff output which is # PURPOSE:        HTML diff. Produces a colourised HTML diff output which is 
 #                much easier to read than standard diff output. #                much easier to read than standard diff output.
 # #
 # SOURCE:        http://www.ginini.com/software/hdiff/ # SOURCE:        www.ginini.com/software/hdiff
 # # 
 #                 hdiff is derived from cvs2html from www.sslug.dk/cvs2html #                 hdiff is derived from cvs2html from www.sslug.dk/cvs2html
 # #
  # MODS:                Thanks to Gordon McKinney to support recursion (-r) and 
  #                 multiple file files per diff run. Also fixed blank context
  #                 lines removing the background fill.
  #
 #------------------------------------------------------------------------------ #------------------------------------------------------------------------------
  
 require 5.000; 
  
 use Getopt::Std; use Getopt::Std;
  
 # #
 # You must use a version of diff that has unified output.  GNU diff has this # 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. # 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 # For most other Unix systems, grab a copy of GNU diff from 
 # ftp://ftp.gnu.org/gnu/diffutils # ftp://ftp.gnu.org/gnu/diffutils
 # #
 $diff = '/usr/bin/diff'; $diff = 'diff';
  
 # #
 # Colors and font to show the diff type of code changes # Colors and font to show the diff type of code changes
 $diffcolorChange     = '#99FF99';    # Changed line(s) (     both    ) $diffcolorChange     = '#99FF99';    # Changed line(s) (     both    )
 $diffcolorRemove     = '#CCCCFF';    # Added line(s)   (  - )  (right) $diffcolorRemove     = '#CCCCFF';    # Added line(s)   (  - )  (right)
 $diffcolorDarkChange = '#99CC99';    # lines, which are empty in change $diffcolorDarkChange = '#99CC99';    # lines, which are empty in change
 $diffcolorLineNum    = '#666666';    # Lines containing line numbers 
  
 #----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
 # END OF CONFIGURABLE OPTIONS # END OF CONFIGURABLE OPTIONS
 #----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
  
 $Version = "2.1.0"; $Version = "1.9.9";
  
 # #
 # Process options # Process options
 # #
  use vars qw($opt_s $opt_r);
  
 # Keep warnings happy getopts('h:f:t:rl:s') or Usage();
 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 ); Usage() unless ( scalar @ARGV == 2 );
  
 $File1 = $ARGV[0]; $File1 = $ARGV[0];
 $File2 = $ARGV[1]; $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) { if ($opt_t) {
     $Title = "$opt_t";     $Title = "$opt_t";
 } }
     $Title = "hdiff output";     $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 "$File1 not found\n" if ( ( !-f $File1 ) && ( !-d $File1 ) );
 die "$File2 not found\n" if ( ( !-f $File2 ) && ( !-d $File2 ) ); die "$File2 not found\n" if ( ( !-f $File2 ) && ( !-d $File2 ) );
  
  
 HtmlFooter(); HtmlFooter();
  
  exit 0;
  
 #-------------------------------------------------------------------------- #--------------------------------------------------------------------------
 sub htmlify { sub htmlify {
     my ( $string, $pr ) = @_;     my ( $string, $pr ) = @_;
     s/\s+$//;     s/\s+$//;
  
     # Expand tabs     # Expand tabs
     if ( defined $tabstop ) {     $string =~ s/\t+/' ' x (length($&) * $tabstop - length($`) % $tabstop)/e
         while ( $string =~ s/\t+/' ' x (length($&) * $tabstop - length($`) % $tabstop)/e ) { }       if ( defined $tabstop );
     } 
  
     # replace <tab> and <space> (§ is to protect us from htmlify)     # replace <tab> and <space> (§ is to protect us from htmlify)
     # gzip can make excellent use of this repeating pattern :-)     # gzip can make excellent use of this repeating pattern :-)
 } }
  
 #------------------------------------------------------------------------------ #------------------------------------------------------------------------------
 sub LineNumText { 
     my ($LineNum) = @_; 
     return '' unless $opt_n; 
     return spacedHtmlText( sprintf( "%3d:", ($$LineNum)++ ) ); 
 } 
  
 #------------------------------------------------------------------------------ 
 sub flush_diff_rows { sub flush_diff_rows {
     my $j;     my $j;
     my ( $leftColRef, $rightColRef, $leftRow, $rightRow ) = @_;     my ( $leftColRef, $rightColRef, $leftRow, $rightRow ) = @_;
 #------------------------------------------------------------------------------ #------------------------------------------------------------------------------
 sub HtmlHeader { sub HtmlHeader {
     if ($opt_h) {     if ($opt_h) {
         my $header_content; 
         open HEADER, $opt_h or die "Can not open $opt_h $!\n";         open HEADER, $opt_h or die "Can not open $opt_h $!\n";
         $header_content = join ( "", <HEADER> );         print <HEADER>;
         $header_content =~ s/#TITLE/$Title/g;         close (HEADER);
         print $header_content; 
         close(HEADER); 
     }     }
     else {     else {
         print qq(<html>\n<head>\n);         print qq(<html>\n<head>\n);
         print qq(<title>$Title</title>\n);         print qq(<title>$Title</title>\n);
         print qq(<style type="text/css">\n<!--\n);         print qq(<style type="text/css">\n<!--\n);
         print qq(BODY { font-family: Versana,Arial,Helvetica }\n);         print qq(BODY { font-family: Versana,Arial,Helvetica }\n);
  
         if ($opt_s) {         if ($opt_s) {
  
  
     print <<EOF;     print <<EOF;
 <table border="0" cellspacing="0" cellpadding="1"> <table border="0" cellspacing="0" cellpadding="1">
 <tr><th align="center" bgcolor="#000000" colspan="2"><font color="#ffffff">$TextLegend</font></th></tr> <tr><th align="center" bgcolor="#000000" colspan="2">Legend</th></tr>
 <tr><td align="center" bgcolor="$diffcolorRemove">$TextAdded</td><td bgcolor="$diffcolorEmpty">&nbsp;</td></tr> <tr><td align="center" bgcolor="$diffcolorRemove">Lines Added</td><td bgcolor="$diffcolorEmpty">&nbsp;</td></tr>
 <tr bgcolor="$diffcolorChange"><td align="center" colspan="2">$TextChanged</td></tr> <tr bgcolor="$diffcolorChange"><td align="center" colspan="2">Lines changed</td></tr>
 <tr><td bgcolor="$diffcolorEmpty">&nbsp;</td><td align="center" bgcolor="$diffcolorAdd">$TextRemoved</td></tr> <tr><td bgcolor="$diffcolorEmpty">&nbsp;</td><td align="center" bgcolor="$diffcolorAdd">Lines Removed</td></tr>
 </table> </table>
  
 <p align="center"><font size="-1"><a href="http://www.ginini.com/software/hdiff/">hdiff - version: $Version</a></font></p> <p align="center"><font size="-1"><a href="http://www.ginini.com/software/hdiff/">hdiff - version: $Version</a></font></p>
     if ($opt_f) {     if ($opt_f) {
         open FOOTER, $opt_f or die "Can not open $opt_f $!\n";         open FOOTER, $opt_f or die "Can not open $opt_f $!\n";
         print <FOOTER>;         print <FOOTER>;
         close(FOOTER);         close (FOOTER);
         return;         return;
     }     }
     else {     else {
 # Function to generate Human readable diff-files # Function to generate Human readable diff-files
 # #
 sub human_readable_diff { sub human_readable_diff {
     my ( $file1, $file2 ) = @_;     my ($file1, $file2) = @_;
  
     my ( $i, $difftxt, $filename, $pathname, $diffopts, $modefile );     my ( $i, $difftxt, $filename, $pathname, $diffopts, $modefile );
  
         $diffopts .= "-r -N ";         $diffopts .= "-r -N ";
     }     }
  
     if ( defined $opt_l ) {     if ($opt_l) {
         $diffopts .= "-U $opt_l ";         $diffopts .= "-U $opt_l ";
     }     }
     else {     else {
             #             #
             if ( $state eq "nodiff" ) {             if ( $state eq "nodiff" ) {
                 print "<tr bgcolor=\"$diffcolorEmpty\" >";                 print "<tr bgcolor=\"$diffcolorEmpty\" >";
                 print "<td colspan=2 align=center><b>$TextNoChange</b></td></tr>";                 print "<td colspan=2 align=center><b>- No viewable Change -</b></td></tr>";
             }             }
  
             if ( $state ne "init" ) {             if ( $state ne "init" ) {
                 # No state change, awaiting +++                 # No state change, awaiting +++
             }             }
             elsif ( $difftxt =~ /^\+\+\+ / ) {             elsif ( $difftxt =~ /^\+\+\+ / ) {
  
                 ($rightfile) = $difftxt =~ /^\+\+\+ (.*)/;                 ($rightfile) = $difftxt =~ /^\+\+\+ (.*)/;
  
                 print qq(<table border="0" cellspacing="0" cellpadding="0" width="100%">\n);                 print qq(<table border="0" cellspacing="0" cellpadding="0" width="100%">\n);
                 print qq(<tr bgcolor="#000000">\n);                 print qq(<tr bgcolor="#000000">\n);
  
                 $CaptionText = $LeftCaptionText; 
                 $CaptionText =~ s/#NAME#/$leftfile/gi; 
                 print qq(<th width="50%" valign="top">);                 print qq(<th width="50%" valign="top">);
                 print qq(<font color="#ffffff">$CaptionText</font>);                 print qq(<font color="#ffffff">$leftfile</font>);
                 print qq(</th>\n);                 print qq(</th>\n);
  
                 $CaptionText = $RightCaptionText; 
                 $CaptionText =~ s/#NAME#/$rightfile/gi; 
                 print qq(<th width="50%" valign="top">);                 print qq(<th width="50%" valign="top">);
                 print qq(<font color="#ffffff">$CaptionText</font>);                 print qq(<font color="#ffffff">$rightfile</font>);
                 print qq(</th>\n</tr>);                 print qq(</th>\n</tr>);
  
                 # Wait for next hunk (@@)                 # Wait for next hunk (@@)
                 print qq(<tr bgcolor="#000000">\n);                 print qq(<tr bgcolor="#000000">\n);
                 print qq(<th width="50%" valign="top">);                 print qq(<th width="50%" valign="top">);
                 print qq(<font color="#ffffff">$difftxt</font>);                 print qq(<font color="#ffffff">$difftxt</font>);
                 print qq(</th>\n);                 print qq(<th>\n);
                 print qq(<th width="50%" valign="top">);                 print qq(<th width="50%" valign="top">);
                 print qq(<font color="#ffffff"><!-- Only found in one directory --></font>);                 print qq(<font color="#ffffff"><!-- Only found in one directory --></font>);
                 print qq(</th>\n</tr>);                 print qq(<th>\n</tr>);
  
                 # No state change, awaiting next 'Only in' or next ---                 # No state change, awaiting next 'Only in' or next ---
             }             }
         }         }
         elsif ( $difftxt =~ /^@@/ ) {         elsif ( $difftxt =~ /^@@/ ) {
  
             # In case of a number of context lines of 0 
             flush_diff_rows \@leftCol, \@rightCol, $leftRow, $rightRow; 
  
             # Hunk, start dumping             # Hunk, start dumping
             #( $funname ) = $difftxt =~ /@@ \-[0-9]+.*\+[0-9]+.*@@(.*)/;             #( $funname ) = $difftxt =~ /@@ \-[0-9]+.*\+[0-9]+.*@@(.*)/;
             $state    = "dump";             $state    = "dump";
             $leftRow  = 0;             $leftRow  = 0;
             $rightRow = 0;             $rightRow = 0;
             ( $LeftLineNum, $RightLineNum ) = $difftxt =~ m/^@@ \-(\d+).*\+(\d+)/; 
  
             if ($opt_N) { 
                 my $LeftText  = $TextLineNumber; 
                 my $RightText = $TextLineNumber; 
                 $LeftText  =~ s/#NUM#/$LeftLineNum/gi; 
                 $RightText =~ s/#NUM#/$RightLineNum/gi; 
                 print "<tr><th width=\"50%\" bgcolor=\"$diffcolorLineNum\"><b>$LeftText</b></th>", "<th width=\"50%\" bgcolor=\"$diffcolorLineNum\">$RightText</th></tr>\n"; 
             } 
  
         }         }
         else {         else {
             ( $diffcode, $rest ) = $difftxt =~ /^([-+ ])(.*)/;             ( $diffcode, $rest ) = $difftxt =~ /^([-+ ])(.*)/;
             ##########             ##########
  
             if ( $diffcode eq '+' ) {             if ( $diffcode eq '+' ) {
                 my $LineNumText = LineNumText($RightLineNum); 
                 if ( $state eq "dump" ) {    # 'change' never begins with '+': just dump out value                 if ( $state eq "dump" ) {    # 'change' never begins with '+': just dump out value
                     print qq(<tr><td bgcolor="$diffcolorEmpty">&nbsp;</td><td bgcolor="$diffcolorAdd">$LineNumText$_</td></tr>\n);                     print qq(<tr><td bgcolor="$diffcolorEmpty">&nbsp;</td><td bgcolor="$diffcolorAdd">$_</td></tr>\n);
                 }                 }
                 else {                       # we got minus before                 else {                       # we got minus before
                     $state = "PreChange";                     $state = "PreChange";
                     $rightCol[ $rightRow++ ] = $LineNumText . $_;                     $rightCol[ $rightRow++ ] = $_;
                 }                 }
             }             }
             elsif ( $diffcode eq '-' ) {             elsif ( $diffcode eq '-' ) {
                 $state = "PreChangeRemove";                 $state = "PreChangeRemove";
                 $leftCol[ $leftRow++ ] = LineNumText($LeftLineNum) . $_;                 $leftCol[ $leftRow++ ] = $_;
             }             }
             else {                           # empty diffcode             else {                           # empty diffcode
                 flush_diff_rows \@leftCol, \@rightCol, $leftRow, $rightRow;                 flush_diff_rows \@leftCol, \@rightCol, $leftRow, $rightRow;
                 print "<tr><td>", LineNumText($LeftLineNum), "$_</td><td>", LineNumText($RightLineNum), "$_</td></tr>\n";                 print "<tr><td>$_</td><td>$_</td></tr>\n";
                 $state    = "dump";                 $state    = "dump";
                 $leftRow  = 0;                 $leftRow  = 0;
                 $rightRow = 0;                 $rightRow = 0;
     # Only output for no diffs     # Only output for no diffs
     #     #
     if ( ( $state eq "nodiff" ) || ( $state eq "init" ) ) {     if ( ( $state eq "nodiff" ) || ( $state eq "init" ) ) {
         print qq(<tr bgcolor="$diffcolorEmpty" >);         print qq(tr bgcolor="$diffcolorEmpty" >);
         print qq(<td colspan="2" align="center"><b>$TextNoChange</b></td></tr>);         print qq(<td colspan="2" align="center"><b>- No viewable Change -</b></td></tr>);
     }     }
  
     close(DIFF);     close(DIFF);
  
     # In case diff does not return 0 (no diff) nor 1 (diffs) 
     die "Error running $diff $diffopts $file1 $file2\n" if ( $? >> 8 ) > 1; 
  
     print "</table>\n\n";     print "</table>\n\n";
     print "<br><br>\n";     print "<br><br>\n";
  
 } }
  
 #------------------------------------------------------------------------------ #------------------------------------------------------------------------------
  # Display script usage
 sub Usage { sub Usage {
     print <<EOF;     print <<EOF;
 Usage: $0 [-rsn] [-e <size>][-f <html footer>] [-h <html header>] [-l <lines>] [-L <Legend>,<Added>,<Changed>,<Removed>,<no change>,<Line>] [-t <title>] [-c <Text>] [-C <Text>] <file1/dir1> <file2/dir2> Usage: $0 [-rs] [-f <html footer>] [-h <html header>] [-l <lines>] [-t <title>] <file1/dir1> <file2/dir2>
  
 -c Caption for file 1 (#NAME# in caption is replaced by file name) 
 -C Caption for file 2 (#NAME# in caption is replaced by file name) 
 -e Expand tabs of 'size' characters (Defaults to 8 if <= 0) 
 -f File to use for HTML footer -f File to use for HTML footer
 -h File to use for HTML header -h File to use for HTML header
 -l Number of context lines -l Num of context lines
 -n Show line numbers in front of each line 
 -N Show line numbers at the beginning of each block (in a separator line), 
      Put a '#NUM#' where you want to see the line number (otherwise it would not appear) 
 -o File to create instead of stdout 
 -r Recursively diff directories 
 -s Small font for printing 
 -t Title heading 
 -L Legends/Text appearing in output, defaults to: 
      "legend,Lines Added,Lines changed,Lines Removed,- No viewable Change -,Line ##NUM#" 
  
 Version: $Version Version: $Version
  


legend
Lines Added 
Lines changed
 Lines Removed

hdiff - version: 2.1.0