Patch to cvsweb?

Bryce Nesbitt (spam account) bryce1 at obviously.com
Sun Nov 23 11:52:24 PST 2003


Bryce Nesbitt wrote:

> Ville Skyttä wrote:
>
>> On Wed, 2003-11-12 at 20:48, Bryce Nesbitt (spam account) wrote:
>>
>>> I implemented an external diff utility.  In this case, it
>>> compares binary application files stored inside a .zip archive.
>>>   
>>
>>
>> Whoo, looks cool.  Could you post the source code (or prefreably a
>> unified diff against CVS HEAD) somewhere?
>

Here's an upgraded version, where you can specify a customized diff
utility for any mime type (e.g. "excel_diff" for ".xls" files).
This also works for sets of files stored in .zip archives.

The patch is supplied against the German FreeBSD mirror, since the
USA mirror seems to be having some sort of problem.

                 -Bryce Nesbitt



-------------- next part --------------
Index: cvsweb.cgi
===================================================================
RCS file: /home/ncvs/projects/cvsweb/cvsweb.cgi,v
retrieving revision 1.215
diff -u -r1.215 cvsweb.cgi
--- cvsweb.cgi	25 Oct 2003 19:16:32 -0000	1.215
+++ cvsweb.cgi	23 Nov 2003 18:54:53 -0000
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -T
+#!/usr/bin/perl -T -IIPC-Run-0.77/lib
 #
 # cvsweb - a CGI interface to CVS trees.
 #
@@ -63,6 +63,7 @@
   $config $allow_version_select
   @CVSrepositories @CVSROOT %CVSROOT %CVSROOTdescr
   %MIRRORS %DEFAULTVALUE %ICONS %MTYPES
+  %DIFF_COMMANDS %DIFF_OPTIONS
   @DIFFTYPES %DIFFTYPES @LOGSORTKEYS %LOGSORTKEYS
   %alltags %fileinfo %tags @branchnames %nameprinted
   %symrev %revsym @allrevisions %date %author @revdisplayorder
@@ -99,7 +100,7 @@
 use File::Path            qw(rmtree);
 use File::Spec::Functions qw(catdir catfile curdir devnull rootdir tmpdir
                              updir);
-use File::Temp            qw(tempdir);
+use File::Temp            qw(tempdir tempfile);
 use IPC::Run              qw(finish timeout);
 use Time::Local           qw(timegm);
 use URI::Escape           qw(uri_escape uri_unescape);
@@ -209,6 +210,7 @@
 sub forbidden_module($);
 sub startproc(@);
 sub runproc(@);
+sub checkout_to_temp($$$);
 
 ##### Start of Configuration Area ########
 
@@ -2232,7 +2234,125 @@
   my @difftype       = @{$difftype->{'opts'}};
   my $human_readable = $difftype->{'colored'};
 
-  # apply special options
+
+  #######################################################################
+  my $mimetype  = getMimeType($fullname);
+  my $cvsname   =  $where;
+  my $cvsmodule =  $module;
+  my $diffutil;
+  my $uncompressutil;
+
+  #######################################################################
+  #     Handle .zip archives, uncompressing first and looking inside
+  #
+  if( $mimetype eq "application/zip" )  {
+    $uncompressutil = "unzip";
+
+    http_header("text/plain");
+
+    # Check if proper binary is in path
+    if( !defined $CMD{$uncompressutil} || $CMD{$uncompressutil} eq "" ) {
+       print "Error: can't find utility '$uncompressutil' to handle mimetype '$mimetype'\n";
+       exit 1;
+    }
+
+    # Create two temporary directories
+    my $tmpexportdir1 = tempdir('cvsweb1.XXXXXXXX', TMPDIR => 1);
+    my $tmpexportdir2 = tempdir('cvsweb2.XXXXXXXX', TMPDIR => 1);
+    $cvsname =~ s/\.diff$//;
+    my ($temp_fn1, $temp_fn2);
+
+    # Check out and uncompress rev1
+    chdir($tmpexportdir1) or exit -1;
+    $temp_fn1 = checkout_to_temp($cvsroot,$cvsname,$rev1);
+    system ($CMD{$uncompressutil}, $temp_fn1);
+
+    # Check out and uncompress rev2
+    $cvsname =~ s/\.diff$//;
+    chdir($tmpexportdir2) or exit -1;
+    $temp_fn2 = checkout_to_temp($cvsroot,$cvsname,$rev2);
+    system ($CMD{$uncompressutil}, $temp_fn2);
+    printf("\n");
+
+    # Examine each extracted file, check if a special diffing command is defined
+    # This was designed to diff a set of related binary files -- please don't
+    # make it diff every file, unless you also add a way to exclude certain files.
+    my $processed_something = 0;
+    my $fname;
+    opendir(DIR, $tmpexportdir2) or die "can't opendir $tmpexportdir2: $!";
+    while (defined($fname = readdir(DIR))) {
+
+	# Check for dangerous "-" at the start of each filename, and untaint
+	if ( $fname =~ m/^\s*-.*/ ) {
+	    printf "Bad filename $fname, skipping\n";
+	    next;
+	}
+	$fname =~ /(.*)/;	# !untaint!
+	$fname = $1;		# !untaint!
+
+        $mimetype  = getMimeType($fname);
+        #print "Saw file $fname | $mimetype\n";
+        if( $diffutil = $DIFF_COMMANDS{$mimetype} ) {
+            if( defined $DIFF_OPTIONS{$mimetype} ) {
+                system($CMD{$diffutil}, $DIFF_OPTIONS{$mimetype}, 
+                       "$tmpexportdir1/$fname", "$tmpexportdir2/$fname");
+                $processed_something = 1;
+            } else {
+                system($CMD{$diffutil}, "$tmpexportdir1/$fname", "$tmpexportdir2/$fname");
+                $processed_something = 1;
+            }
+        }
+    }
+    closedir(DIR);
+
+    # If we did not diff anything above, then just do a standard "diff -r"
+    if( !$processed_something ) {
+        system ($CMD{diff}, "-r", "--", "$tmpexportdir1", "$tmpexportdir2");
+    }
+    
+    # Delete temporary files & directories
+    system ($CMD{rm}, "-r", "$tmpexportdir1");
+    system ($CMD{rm}, "-r", "$tmpexportdir2");
+    system ($CMD{rm}, "$temp_fn1");
+    system ($CMD{rm}, "$temp_fn2");
+    exit;
+    }
+
+
+  #######################################################################
+  #     Check for MIME type that uses a non-standard diff
+  #
+  if( $diffutil = $DIFF_COMMANDS{$mimetype} )  {
+    my ($temp_fn1, $temp_fn2);
+    http_header("text/plain");
+ 
+    # Create two temporary files with the two revisions
+    $cvsname =~ s/\.diff$//;
+    $temp_fn1 = checkout_to_temp($cvsroot,$cvsname,$rev1);
+    $temp_fn2 = checkout_to_temp($cvsroot,$cvsname,$rev2);
+
+    # Make sure we have a command to execute
+    if( !defined $CMD{$diffutil} || $CMD{$diffutil} eq "" ) {
+       print "Error: can't find utility '$diffutil' to handle mimetype '$mimetype'\n";
+       exit 1;
+    }
+
+    # Execute chosen diff binary
+    if( defined $DIFF_OPTIONS{$mimetype} ) {
+	system($CMD{$diffutil}, $DIFF_OPTIONS{$mimetype}, "$temp_fn1", "$temp_fn2");
+    } else {
+	system($CMD{$diffutil}, "$temp_fn1", "$temp_fn2");
+    }
+
+    # Delete temporary files
+    system ($CMD{rm}, "$temp_fn1");
+    system ($CMD{rm}, "$temp_fn2");
+    exit;
+    }
+
+
+  #######################################################################
+  # regular diff: apply special options
   if ($showfunc) {
     push @difftype, '-p' if $f ne 's';
 
@@ -2322,7 +2442,7 @@
   # - Add "no differences found" if the diff command supplied no output.
   #
   #*** src/sys/netinet/tcp_output.c     1995/11/03 22:08:08     1.16
-  #--- src/sys/netinet/tcp_output.c     1995/12/05 17:46:35     1.17 RELENG_2_1_0
+  #--- src/sys/netinet/tcp_output.c     1995/12/05 17:46:35     1.17 REL_2_1_0
   # (bogus example, but...)
   #
   my ($f1, $f2);
@@ -2355,6 +2475,7 @@
 }
 
 
+
 ###############################
 # Show Logs ..
 ###############################
@@ -3106,8 +3227,8 @@
   print "<p>\n ";
   print &link($backicon, "$backurl#$filename"), " <b>Up to ",
     &clickablePath($upwhere, 1), "</b>\n</p>\n";
-  print "<p>\n ";
-  print &link('Request diff between arbitrary revisions', '#diff');
+  #print "<p>\n ";
+  #print &link('Request diff between arbitrary revisions', '#diff');
   if ($allow_cvsgraph) {
     print ' / ', &graph_link('', 'Display revisions graphically');
   }
@@ -4406,6 +4527,23 @@
 {
   my ($o) = @_;
   CLOSE($o);
+}
+
+sub checkout_to_temp($$$)
+{
+my  ($cvsroot,$cvsname,$rev) = @_;
+my  ($pipe_fh);
+
+    # pipe given cvs file into a temporary place
+    my ($temp_fh, $temp_fn) = tempfile();
+    if (!open($pipe_fh, "-|")) {    	# start child process
+    	exec ($CMD{cvs}, @cvs_options, '-Qd', $cvsroot, 'co', "-p", "-r$rev", $cvsname);
+	}			# end child process
+    while(<$pipe_fh>) {
+	print $temp_fh $_;
+	}
+    close $temp_fh;
+    return $temp_fn;
 }
 
 # Local variables:
Index: cvsweb.conf
===================================================================
RCS file: /home/ncvs/projects/cvsweb/cvsweb.conf,v
retrieving revision 1.64
diff -u -r1.64 cvsweb.conf
--- cvsweb.conf	30 Oct 2003 20:09:18 -0000	1.64
+++ cvsweb.conf	23 Nov 2003 18:54:53 -0000
@@ -20,11 +20,11 @@
 #   (g)tar, zip (if you enable $allow_tar)
 #   cvsgraph (if you enable $allow_graph)
 #
-$command_path = '/bin:/usr/bin:/usr/local/bin';
+$command_path = '/bin:/usr/bin:/usr/local/bin:/cgi/home/bryce/www.obviously.com/gis/cvsweb/bin';
 
 # Search the above directories for each command (prefer gtar over tar).
 #
-for (qw(cvs rlog rcsdiff gzip gtar zip cvsgraph enscript)) {
+for (qw(cvs rlog rcsdiff gzip gtar zip cvsgraph enscript rm mv unzip diff shpdiff)) {
 	$CMD{$_} = search_path($_);
 }
 $CMD{tar}   = delete($CMD{gtar}) if $CMD{gtar};
@@ -42,7 +42,8 @@
 # 'symbolic_name' => ['Name to display',  '/path/to/cvsroot']
 #
 @CVSrepositories = (
-        'local'   => ['Local Repository', '/home/cvs'],
+        'local'   => ['Local Repository', '/cgi/home/bryce/www.obviously.com/gis_cvsroot/'],
+        'test'    => ['Test Repository',  '/cgi/home/bryce/www.obviously.com/cvsroot_test/'],
 #       'freebsd' => ['FreeBSD',          '/home/ncvs'],
 #       'openbsd' => ['OpenBSD',          '/home/ncvs'],
 #       'netbsd'  => ['NetBSD',           '/home/ncvs'],
@@ -220,7 +221,7 @@
 
 # An URL where to find the CSS.
 #
-$cssurl = '/css/cvsweb.css';
+$cssurl = 'css/cvsweb.css';
 
 # the length to which the last logentry should
 # be truncated when shown in the directory view
@@ -318,10 +319,21 @@
 #
 $inputTextSize = 12;
 
+###########################################################################
+#   Custom diff type based on mime type
+#   (used for comparing binary files such as spreadhseets, images)
+###########################################################################
+%DIFF_COMMANDS = (
+        "application/vnd.arcview"   => "shpdiff",
+        "application/vnd.ms-excel"  => "excel_diff",
+);
+%DIFF_OPTIONS = (
+        "application/vnd.ms-excel"  => "-vvv",
+);
+
 ##############
 # Mime Types
 ##############
-
 # The MIME type lookup works like this:
 # 1) Look up from %MTYPES below with the file name extension (suffix).
 # 2) If not found, use the MIME::Types(3) module if it's available.
@@ -338,13 +350,24 @@
 %MTYPES = (
 	"html"  => "text/html",
 	"shtml" => "text/html",
+
 	"gif"   => "image/gif",
 	"jpeg"  => "image/jpeg",
 	"jpg"   => "image/jpeg",
 	"png"   => "image/png",
 	"xpm"   => "image/xpm",
+
+	"zip"   => "application/zip",
+	"tgz"   => "application/x-tgz",
+	"tar"   => "application/x-tar",
+	"gz"    => "application/x-gzip",
+        "bz2"   => "application/bz2",
+
+	"shp"   => "application/vnd.arcview",
+	"xls"   => "application/vnd.ms-excel",
 #	"*"     => "text/plain",
 );
+
 
 # The traditional mime.types file, eg. the one from Apache is fine.
 # See above where this gets used.


More information about the freebsd-cvsweb mailing list