#!/usr/bin/perl -s					      |	#!/usr/local/bin/perl -s
#							      (
# cvsweb - a CGI interface to the CVS tree.		      (
#							      (
# Written by Bill Fenner <fenner@parc.xerox.com> on his own t |	# Note, this is under RCS control in /home/fenner:
# Insert BSD copyright here.				      |	# $Header: /usr/cvs_base/cvs-web/cvsweb.pl,v 1.12 1998/02/18 
							      >	#
							      >	# Written by Bill Fenner <fenner@freebsd.org>.
							      >	# Some modifications by Martin Cleaver <Martin.Cleaver@BCS.or
#							      (
#HTTP_USER_AGENT: Mozilla/1.1N (X11; I; SunOS 4.1.3_U1 sun4m) (
#SERVER_NAME: www.freebsd.org				      (
#QUERY_STRING: baz					      (
#SCRIPT_FILENAME: /usr/local/www/cgi-bin/env.pl		      (
#SERVER_PORT: 80					      (
#HTTP_ACCEPT: */*, image/gif, image/x-xbitmap, image/jpeg     (
#SERVER_PROTOCOL: HTTP/1.0				      (
#HTTP_COOKIE: s=beta26429821397802167			      (
#PATH_INFO: /foo/bar					      (
#REMOTE_ADDR: 13.1.64.94				      (
#DOCUMENT_ROOT: /usr/local/www/data/			      (
#PATH: /sbin:/bin:/usr/sbin:/usr/bin			      (
#PATH_TRANSLATED: /usr/local/www/data//foo/bar		      (
#GATEWAY_INTERFACE: CGI/1.1				      (
#REQUEST_METHOD: GET					      (
#SCRIPT_NAME: /cgi-bin/env.pl				      (
#SERVER_SOFTWARE: Apache/1.0.0				      (
#REMOTE_HOST: beta.xerox.com				      (
#SERVER_ADMIN: webmaster@freebsd.org			      (
#							      |
							      >	# Known deficiencies:
							      >	#   Moved page redirecting to cvswebedit.pl doesn't work
							      >	#              [LOW - user should never see the page]
							      >
							      >
							      >	package main;
							      >	use FileHandle;
							      >	use File::Path;
							      >	use CGI::Base;
							      >	use CGI::Request;
							      >	use strict;
							      >	use Cgilog;
							      >
require 'timelocal.pl';					      (
require 'ctime.pl';					      (
							      (
$hsty_base = "";					      |	#
require 'cgi-style.pl';					      |	# $req - CGI request object.
#&get_the_source;					      |	#
							      <
%CVSROOT = (						      <
	    'freebsd', '/home/ncvs',			      <
	    'openbsd', '/home/OpenBSD/cvs',		      <
	    'learn', '/c/learncvs',			      <
	    );						      <
							      <
$cvstreedefault = 'freebsd';				      <
$cvstree = $cvstreedefault;				      <
$cvsroot = $CVSROOT{"$cvstree"} || "/home/ncvs";	      <
							      <
							      <
$intro = "						      <
This is a WWW interface to the FreeBSD CVS tree.	      <
You can browse the file hierarchy by picking directories      <
(which have slashes after them, e.g. <b>src/</b>).	      <
If you pick a file, you will see the revision history	      <
for that file.						      <
Selecting a revision number will download that revision of    <
the file.  There is a link at each revision to display	      <
diffs between that revision and the previous one, and	      <
a form at the bottom of the page that allows you to	      <
display diffs between arbitrary revisions.		      <
<p>							      <
If you would like to use this CGI script on your own web serv <
CVS tree, see <A HREF=\"http://www.freebsd.org/~fenner/cvsweb <
the CVSWeb distribution site</A>.			      <
<p>							      <
Please send any suggestions, comments, etc. to		      <
<A HREF=\"mailto:fenner\@freebsd.org\">Bill Fenner &lt;fenner <
";							      <
$shortinstr = "						      <
Click on a directory to enter that directory. Click on a file <
its revision history and to get a			      <
chance to display diffs between revisions. 		      <
";							      <
							      (
$verbose = $v;						      |	my $req = new CGI::Request;
($where = $ENV{'PATH_INFO'}) =~ s|^/||;			      <
$where =~ s|/$||;					      <
($scriptname = $ENV{'SCRIPT_NAME'}) =~ s|^/?|/|;	      <
$scriptname =~ s|/$||;					      <
$scriptwhere = $scriptname . '/' . $where;		      <
$scriptwhere =~ s|/$||;					      <
							      (
if ($query = $ENV{'QUERY_STRING'}) {			      |	##########################
    foreach (split(/&/, $query)) {			      |	#
	s/%(..)/sprintf("%c", hex($1))/ge;	# unquote %-q |	# Configuration Settings.
	if (/(\S+)=(.*)/) {				      |	#   Global variables for package main
	    $input{$1} = $2;				      <
	} else {					      <
	    $input{$_}++;				      <
	}						      <
    }							      <
    $query = "?" . $query;				      <
}							      <
							      (
							      >	use vars qw($title $h1 $intro $shortinstr $ignore $v);
							      >	use vars qw($cvsroot $rcsbinaries $backicon $diricon $textico
							      >	use vars qw(%metaprogs $cvstree $cvstreedefault $cvswebedit_u
							      >	use vars qw($cvswebview_dbg);
							      (
$config = '/usr/local/etc/cvsweb';			      <
do "$config" if -f $config;				      <
							      (
if ($input{'cvsroot'}) {				      |	{ # disable warnings
    if ($CVSROOT{$input{'cvsroot'}}) {			      |	    local ($^W) = 0;
	$cvstree = $input{'cvsroot'};			      |	    require 'cvswebconfig.pl'; # Settings per-site
	$cvsroot = $CVSROOT{"$cvstree"};		      |	    require 'cgi-style.pl';    # Settings for the style of ht
    }							      <
}							      (
do "$config-$cvstree" if -f "$config-$cvstree";		      <
							      (
$fullname = $cvsroot . '/' . $where;			      |	##### End configuration section
							      >
							      >	init_output($cvswebview_dbg);
							      >	do_log("test");
							      >
							      >	###############################################
							      >	#
							      >	# Global variables for package main
							      >	#
							      >
							      >	my $cvswebversion = '$Revision: 1.1 $ MC';
							      >	my $updir;      # Last directory we were in.
							      >	my $verbose = $v;
							      >
							      >	my $where = $req->cgi->var('PATH_INFO');
							      >	$where =~ s|^/||;
							      >	$where =~ s|/$||;
							      >
							      >	my $fullname = $cvsroot . '/' . $where;
							      >
							      >	my $scriptname = $req->cgi->var('SCRIPT_NAME');
							      >	$scriptname =~ s|^/?|/|;
							      >	$scriptname =~ s|/$||;
							      >
							      >	my $scriptwhere = $scriptname . '/' . $where;
							      >	$scriptwhere =~ s|/$||;
							      >
							      >	my $querystring =  $req->cgi->var('QUERY_STRING');
							      >	my $query = '?'.$querystring; #FIXME - check correct
							      >
							      >	###############################################
							      (
if (!-d $cvsroot) {					      (
	&fatal("500 Internal Error",'$CVSROOT not found!<P>Th |	    fatal("500 Internal Error",'cvsroot \''.$cvsroot.'\' not 
}							      (
							      (
							      >	###############################################
							      (
if (-d $fullname) {					      |	do_log("FULLNAME=$fullname\nSCRIPTWHERE=$scriptwhere\nQUERYST
	opendir(DIR, $fullname) || &fatal("404 Not Found","$w |
	@dir = readdir(DIR);				      |	handle_path();
	closedir(DIR);					      |	exit 0;
	if ($where eq '') {				      |
	    print &html_header("FreeBSD CVS Repository");     |	###############################################
	    print $intro;				      |
	} else {					      |	sub handle_path {
	    print &html_header("/$where");		      |	    my ($newname, $newplace, @files, $xtra, $module);
	    print $shortinstr;				      |	    if (-d $fullname) {
	}						      |		display_directory($fullname);
	print "<p>Current directory: <b>/$where</b>\n";	      |		exit 0;
	print "<P><HR NOSHADE>\n";			      |	    } elsif (-f $fullname . ',v') {
	# Using <MENU> in this manner violates the HTML2.0 sp |		display_file($fullname);
	# provides the results that I want in most browsers.  |		exit 0;
	# case of layout spooging up HTML.		      |	    } elsif ($fullname =~ s/\.diff$// && -f $fullname . ",v" 
	print "<MENU>\n";				      |		     $req->param('r1') && $req->param('r2')) {
	lookingforattic:				      |		
	for ($i = 0; $i <= $#dir; $i++) {		      <
		if ($dir[$i] eq "Attic") {		      <
			last lookingforattic;		      <
		}					      <
	}						      <
	$haveattic = 1 if ($i <= $#dir);		      <
	if (!$input{"showattic"} && ($i <= $#dir) &&	      <
				opendir(DIR, $fullname . "/At <
		splice(@dir, $i, 1,			      <
			grep((s|^|Attic/|,!m|/\.|), readdir(D <
		closedir(DIR);				      <
	}						      <
	# Sort without the Attic/ pathname.		      <
	foreach (sort {($c=$a)=~s|.*/||;($d=$b)=~s|.*/||;($c  <
	    if ($_ eq '.') {				      <
		next;					      <
	    }						      <
	    if (s|^Attic/||) {				      <
		$attic = " (in the Attic)";		      <
	    } else {					      <
		$attic = "";				      <
	    }						      <
	    if ($_ eq '..') {				      <
		next if ($where eq '');			      <
		($updir = $scriptwhere) =~ s|[^/]+$||;	      <
		print "<IMG SRC=\"/icons/back.gif\"> ",	      <
		    &link("Previous Directory",$updir . $quer <
#		print "<IMG SRC=???> ",			      <
#		    &link("Directory-wide diffs", $scriptwher <
	    } elsif (-d $fullname . "/" . $_) {		      <
		print "<IMG SRC=\"/icons/dir.gif\"> ",	      <
		    &link($_ . "/", $scriptwhere . '/' . $_ . <
			    $attic, "<BR>";		      <
	    } elsif (s/,v$//) {				      <
# TODO: add date/time?  How about sorting?		      <
		print "<IMG SRC=\"/icons/text.gif\"> ",	      <
		    &link($_, $scriptwhere . '/' .	      <
			    ($attic ? "Attic/" : "") . $_ . $ <
			    $attic, "<BR>";		      <
	    }						      <
	}						      <
	print "</MENU>\n";				      <
	if ($input{"only_on_branch"}) {			      <
	    print "<HR><FORM METHOD=\"GET\" ACTION=\"${script <
	    print "Currently showing only branch $input{'only <
	    $input{"only_on_branch"}="";		      <
	    foreach $k (keys %input) {			      <
		print "<INPUT TYPE=hidden NAME=$k VALUE=$inpu <
	    }						      <
	    print "<INPUT TYPE=SUBMIT VALUE=\"Show all branch <
	    print "</FORM>\n";				      <
	}						      <
	$formwhere = $scriptwhere;			      <
	$formwhere =~ s|Attic/?$|| if ($input{"showattic"});  <
	if ($haveattic) {				      <
		print "<HR><FORM METHOD=\"GET\" ACTION=\"${fo <
		$input{"showattic"}=!$input{"showattic"};     <
		foreach $k (keys %input) {		      <
		    print "<INPUT TYPE=hidden NAME=$k VALUE=$ <
		}					      <
		print "<INPUT TYPE=SUBMIT VALUE=\"";	      <
		print ($input{"showattic"} ? "Show" : "Hide") <
		print " attic directories\">\n";	      <
		print "</FORM>\n";			      <
	}						      <
	print &html_footer;				      <
	print "</BODY></HTML>\n";			      <
} elsif (-f $fullname . ',v') {				      <
	if ($input{'rev'} =~ /^[\d\.]+$/) {		      <
		&checkout($fullname, $input{'rev'});	      <
		exit;					      <
	}						      <
	if ($input{'r1'} && $input{'r2'}) {		      <
		&dodiff($fullname, $input{'r1'}, $input{'tr1' <
			$input{'r2'}, $input{'tr2'}, $input{' <
		exit;					      <
	}						      <
print("going to dolog($fullname)\n") if ($verbose);	      <
	&dolog($fullname);				      <
} elsif ($fullname =~ s/\.diff$// && -f $fullname . ",v" &&   <
				$input{'r1'} && $input{'r2'}) <
	# Allow diffs using the ".diff" extension	      (
	# so that browsers that default to the URL	      (
	# for a save filename don't save diff's as	      (
	# e.g. foo.c					      (
	&dodiff($fullname, $input{'r1'}, $input{'tr1'},	      |		
		$input{'r2'}, $input{'tr2'}, $input{'f'});    |		# Note diffs are normally handled in display_file();
							      >		display_diff_between_revisions($fullname);
	exit;						      (
} elsif (($newname = $fullname) =~ s|/([^/]+)$|/Attic/$1| &&  |	    } elsif (($newname = $fullname) =~ s|/([^/]+)$|/Attic/$1|
				 -f $newname . ",v") {	      |		     -f $newname . ",v") {
	# The file has been removed and is in the Attic.      (
	# Send a redirect pointing to the file in the Attic.  (
	($newplace = $scriptwhere) =~ s|/([^/]+)$|/Attic/$1|; (
	&redirect($newplace);				      |		redirect($newplace);
	exit;						      (
} elsif (0 && (@files = &safeglob($fullname . ",v"))) {	      |	    } elsif (0 && (@files = safeglob($fullname . ",v"))) {
	print "Content-type: text/plain\n\n";		      |		output "Content-type: text/plain\n\n";
	print "You matched the following files:\n";	      |		output "You matched the following files:\n";
	print join("\n", @files);			      |		output join("\n", @files);
	# Find the tags from each file			      (
	# Display a form offering diffs between said tags     (
} else {						      |	    } else {
	# Assume it's a module name with a potential path fol (
	$xtra = $& if (($module = $where) =~ s|/.*||);	      (
	# Is there an indexed version of modules?	      (
	if (open(MODULES, "$cvsroot/CVSROOT/modules")) {      (
		while (<MODULES>) {			      |		    while (<MODULES>) {
			if (/^(\S+)\s+(\S+)/o && $module eq $ |			if (/^(\S+)\s+(\S+)/o && $module eq $1
				&& -d "${cvsroot}/$2" && $mod |			    && -d "${cvsroot}/$2" && $module ne $2) {
				&redirect($scriptname . '/' . |			    redirect($scriptname . '/' . $2 . $xtra);
			}				      <
		}					      (
							      >		    }
	}						      (
	&fatal("404 Not Found","$where: no such file or direc |		do_log("Nothing matched");
							      >		fatal("404 Not Found","$where: no such file or direct
							      >	    }
}							      (
							      (
							      >
							      >	sub display_header {
							      >		output "Content-type: text/html\n\n";
							      >		output "<HTML><HEAD><TITLE>${title}: /$where</TITLE><
							      >		output "<BODY>\n";
							      >		output "<!-- Generated by cvsweb version $cvswebversi
							      >		output "<H1>$h1</H1>\n<HR>\n";
							      >		if ($where eq '') {
							      >		    output $intro;
							      >		} else {
							      >		    output $shortinstr;
							      >		}
							      >		output "<p>Current directory: <b>/$where</b>\n";
							      >		output "<P><HR>\n";
							      >
							      >	}
							      >
							      >
sub htmlify {						      (
	local($string, $pr) = @_;			      |		my ($string) = @_;
							      (
	$string =~ s/&/&amp;/g;				      (
	$string =~ s/</&lt;/g;				      (
	$string =~ s/>/&gt;/g;				      (
							      (
	if ($pr) {					      |		# MC? - what does this do?
		$string =~ s|\bpr(\W+[a-z]+/\W*)(\d+)|<A HREF |	#        if ($pr) {
	}						      |	#                $string =~ s|\bpr(\W+[a-z]+/\W*)(\d+)|<A HRE
							      |	#        }
	$string;					      |	 
							      >		return $string;
}							      (
							      (
sub link {						      |	sub htlink {
	local($name, $where) = @_;			      |		my ($name, $where) = @_;
							      |		return "<A HREF=\"$where\">$name</A>\n";
	"<A HREF=\"$where\">$name</A>\n";		      <
}							      (
							      (
sub revcmp {						      (
	local($rev1, $rev2) = @_;			      |		my ($rev1, $rev2) = @_;
	local(@r1) = split(/\./, $rev1);		      |		my (@r1) = split(/\./, $rev1);
	local(@r2) = split(/\./, $rev2);		      |		my (@r2) = split(/\./, $rev2);
	local($a,$b);					      |		local ($a,$b);
							      (
	while (($a = shift(@r1)) && ($b = shift(@r2))) {      (
	    if ($a != $b) {				      (
		return $a <=> $b;			      (
	    }						      (
	}						      (
	if (@r1) { return 1; }				      (
	if (@r2) { return -1; }				      (
	return 0;					      (
}							      (
							      (
sub fatal {						      (
	local($errcode, $errmsg) = @_;			      |		my ($errcode, $errmsg) = @_;
	print "Status: $errcode\n";			      |		output "Status: $errcode\n";
	print &html_header("Error");			      |		output html_header("Error");
#	print "Content-type: text/html\n";		      |	#	output "Content-type: text/html\n"; #DELME
#	print "\n";					      |	#	output "\n";
#	print "<HTML><HEAD><TITLE>Error</TITLE></HEAD>\n";    |	#	output "<HTML><HEAD><TITLE>Error</TITLE></HEAD>\n";
#	print "<BODY>Error: $errmsg</BODY></HTML>\n";	      |	#	output "<BODY>Error: $errmsg</BODY></HTML>\n";
	print "Error: $errmsg\n";			      |		output "Error: $errmsg\n";
	print &html_footer;				      |		output html_footer();
	exit(1);					      (
}							      (
							      (
sub redirect {						      (
	local($url) = @_;				      |	        my($url) = @_;
	print "Status: 301 Moved\n";			      |	        output "Status: 301 Moved\n";
	print "Location: $url\n";			      |	        output "Location: $url\n";
	print &html_header("Moved");			      |	        output html_header("Moved");
#	print "Content-type: text/html\n";		      |	#       output "Content-type: text/html\n"; #DELME
#	print "\n";					      |	#       output "\n";
#	print "<HTML><HEAD><TITLE>Moved</TITLE></HEAD>\n";    |	#       output "<HTML><HEAD><TITLE>Moved</TITLE></HEAD>\n";
#	print "<BODY>This document is located <A HREF=$url>he |	#       output "<BODY>This document is located <A HREF=$url>h
	print "This document is located <A HREF=$url>here</A> |	        output "This document is located <A HREF=$url>here</A
	print &html_footer;				      |	        output &html_footer;
	exit(1);					      |	        exit(1);
}							      (
							      |	 
sub safeglob {						      (
	local($filename) = @_;				      |	        my($filename) = @_;
	local($dirname);				      |	        my($dirname);
	local(@results);				      |	        my(@results);
							      |	 
	($dirname = $filename) =~ s|/[^/]+$||;		      |	        ($dirname = $filename) =~ s|/[^/]+$||;
	$filename =~ s|.*/||;				      |	        $filename =~ s|.*/||;
							      |
	if (opendir(DIR, $dirname)) {			      |		my ($glob, $t);
		$glob = $filename;			      |	        if (opendir(DIR, $dirname)) {
	#	transform filename from glob to regex.  Deal  |	                $glob = $filename;
	#	[, {, ?, * as glob chars		      |	        #       transform filename from glob to regex.  Deal 
	#	make sure to escape all other regex chars     |	        #       [, {, ?, * as glob chars
		$glob =~ s/([\.\(\)\|\+])/\\$1/g;	      |	        #       make sure to escape all other regex chars
		$glob =~ s/\*/.*/g;			      |	                $glob =~ s/([\.\(\)\|\+])/\\$1/g;
		$glob =~ s/\?/./g;			      |	                $glob =~ s/\*/.*/g;
		$glob =~ s/{([^}]+)}/($t = $1) =~ s-,-|-g; "( |	                $glob =~ s/\?/./g;
		foreach (readdir(DIR)) {		      |	                $glob =~ s/{([^}]+)}/($t = $1) =~ s-,-|-g; "(
			if (/^${glob}$/) {		      |	                foreach (readdir(DIR)) {
				push(@results, $dirname . "/" |	                        if (/^${glob}$/) {
			}				      |	                                push(@results, $dirname . "/"
		}					      |	                        }
	}						      |	                }
							      >	        }
							      >	 
							      >	        @results;
							      >	}
							      (
	@results;					      |
							      >	sub display_file {
							      >	    do_log("FULLNAME=$fullname\n");
							      >	# Okay, the file exists...
							      >	    
							      >	    my $method = $ENV{'REQUEST_METHOD'};
							      >	    do_log("METHOD=".$method."\n");
							      >	    
							      >	    if ($method eq 'POST') {
							      >		upload_and_unlock('TEST', $where, 'REV'); # FIXME
							      >	    }
							      >
							      >	    do_log($req->param());
							      >	    if ($req->param()) {
							      >		if ($req->param('meta')) {
							      >		    display_meta($fullname, $req->param('rev'));
							      >		    exit 0;
							      >		}
							      >		
							      >		if ($req->param('rev')) {
							      >		    display_single_revision($fullname);
							      >		    exit 0;
							      >		}
							      >		
							      >		if ($req->param('r1')) {
							      >		    display_diff_between_revisions($fullname);
							      >		    exit 0;
							      >		}
							      >			    
							      >	    } else {
							      >		do_log("No parameters, showing log");
							      >		display_log($fullname);
							      >	    }
							      >	}
							      >
							      >	sub display_directory {
							      >	        opendir(DIR, $fullname) || fatal("404 Not Found","$wh
							      >	        my @dir = readdir(DIR);
							      >	        closedir(DIR);
							      >	        if ($where eq '') {
							      >	            output html_header($h1); #FIXME bad name
							      >	            output $intro;
							      >	        } else {
							      >	            output html_header("/$where");
							      >	            output $shortinstr;
							      >	        }
							      >	        output "<p>Current directory: <b>/$where</b>\n";
							      >	        output "<P><HR NOSHADE>\n";
							      >	        # Using <MENU> in this manner violates the HTML2.0 sp
							      >	        # provides the results that I want in most browsers. 
							      >	        # case of layout spooging up HTML.
							      >	        output "<MENU>\n";
							      >
							      >		my ($i, $haveattic);
							      >	        lookingforattic:
							      >	        for ($i = 0; $i <= $#dir; $i++) {
							      >	                if ($dir[$i] eq "Attic") {
							      >	                        last lookingforattic;
							      >			    }
							      >		    }
							      >		$haveattic = 1 if ($i <= $#dir);
							      >		if (!$req->param("showattic") && ($i <= $#dir) &&
							      >		    opendir(DIR, $fullname . "/Attic")) {
							      >		    splice(@dir, $i, 1,
							      >			   grep((s|^|Attic/|,!m|/\.|), readdir(DIR)))
							      >		    closedir(DIR);
							      >		}
							      >
							      >		my ($c, $d, $attic);
							      >	        # Sort without the Attic/ pathname.
							      >	        foreach (sort {($c=$a)=~s|.*/||;($d=$b)=~s|.*/||;($c 
							      >	            if ($_ eq '.') {
							      >	                next;
							      >	            }
							      >	            if (s|^Attic/||) {
							      >	                $attic = " (in the Attic)";
							      >	            } else {
							      >	                $attic = "";
							      >	            }
							      >	            if ($_ eq '..') {
							      >	                next if ($where eq '');
							      >	                ($updir = $scriptwhere) =~ s|[^/]+$||;
							      >	                output "<IMG SRC=\"$backicon\"> ",
							      >	                    htlink("Previous Directory",$updir . $que
							      >	#               output "<IMG SRC=???> ",
							      >	#                   htlink("Directory-wide diffs", $scriptwhe
							      >	            } elsif (-d $fullname . "/" . $_) {
							      >	                output "<IMG SRC=\"$diricon\"> ",
							      >	                    htlink($_ . "/", $scriptwhere . '/' . $_ 
							      >	                            $attic, "<BR>";
							      >	            } elsif (s/,v$//) {
							      >
							      >	#FIXME: $query?
							      >	#FIXME: icons? binary files?
							      >
							      >	# TODO: add date/time?  How about sorting?
							      >			my $file_attic = ($attic ? "Attic/" : "").$_;
							      >	                output "<IMG SRC=\"$texticon\"> ";
							      >	                output " ". htlink($_, $scriptwhere.'/'.$file
							      >	                output " ".htlink("(text)", $scriptwhere.'/'.
							      >			output " ".htlink("(edit)","$cvswebedit_url/$
							      >	                output '<BR>';
							      >	            }
							      >	        }
							      >
							      >		my $k;
							      >	        output "</MENU>\n";
							      >	        if ($req->param("only_on_branch")) {
							      >	            output "<HR><FORM METHOD=\"GET\" ACTION=\"${scrip
							      >	            output "Currently showing only branch $req->param
							      >	            $req->param("only_on_branch");
							      >	            foreach $k ($req->param()) { #FIXME check
							      >	                output "<INPUT TYPE=hidden NAME=$k VALUE=$req
							      >	            }
							      >	            output "<INPUT TYPE=SUBMIT VALUE=\"Show all branc
							      >	            output "</FORM>\n";
							      >	        }
							      >	        my $formwhere = $scriptwhere;
							      >	        $formwhere =~ s|Attic/?$|| if ($req->param("showattic
							      >	        if ($haveattic) {
							      >	                output "<HR><FORM METHOD=\"GET\" ACTION=\"${f
							      >	                $req->param("showattic", !$req->param("showat
							      >	                foreach $k ($req->param()) {
							      >	                    output "<INPUT TYPE=hidden NAME=$k VALUE=
							      >	                }
							      >	                output "<INPUT TYPE=SUBMIT VALUE=\"";
							      >	                output ($req->param("showattic") ? "Show" : "
							      >	                output " attic directories\">\n";
							      >	                output "</FORM>\n";
							      >	        }
							      >	        output html_footer();
							      >	        output "</BODY></HTML>\n";
							      >
							      >	}
							      >
							      >	sub display_single_revision ($) {
							      >	        my ($fullname) = @_;
							      >		my $rev = $req->param('rev');
							      >
							      >	#	output ">$rev\n";
							      >
							      >		my $fh = get_rev($fullname, $rev);
							      >		$| = 1;
							      >		if ($req->param('content-type')) {
							      >	            output "Content-type: ".$req->param('content-type
							      >		} 
							      >		if ($req->param('content-encoding')) {
							      >		    if ($req->param('content-encoding') eq "x-gzip" )
							      >	               output "Content-encoding: x-gzip\n\n";
							      >		       open(GZIP, "|gzip -1 -c");	# need lightw
							      >		       print GZIP <$fh>;
							      >		       close(GZIP);
							      >		    }
							      >		} else {
							      >		    output "\n";
							      >		    output <$fh>;
							      >		}
							      >		close($fh);
}							      (
							      (
sub checkout {						      |	sub get_rev {
	local($fullname, $rev) = @_;			      |	    my ($fullname, $rev) = @_;
							      (
	open(RCS, "co -p$rev '$fullname' 2>&1 |") ||	      <
	    &fail("500 Internal Error", "Couldn't co: $!");   <
# /home/ncvs/src/sys/netinet/igmp.c,v  -->  standard output   (
# or							      <
# /home/ncvs/src/sys/netinet/igmp.c,v  -->  stdout	      <
# revision 1.1.1.2					      (
# /*							      (
	$_ = <RCS>;					      |
	if (/^(\S+),v\s+-->\s+st(andar)?d ?out(put)?\s*$/o && |	    if ($rev eq "HEAD") {
	    # As expected				      |		$rev = "";
	} else {					      |	    }
	    &fatal("500 Internal Error",		      |
		"Unexpected output from co: $_");	      |	    my $fh = new FileHandle("co -p$rev '$fullname' 2>&1 |") |
	}						      |		fail("500 Internal Error", "Couldn't co: $!");
	$_ = <RCS>;					      |	# /home/ncvs/src/sys/netinet/igmp.c,v  -->  standard output
	if (/^revision\s+$rev\s*$/) {			      |	# revision 1.1.1.2
	    # As expected				      |	# /*
	} else {					      |	    $_ = <$fh>;
	    &fatal("500 Internal Error",		      |	    if (/^(\S+),v\s+-->\s+st(andar)?d ?out(put)?\s*$/o && $1 
		"Unexpected output from co: $_");	      |		# As expected
							      >
							      >	    } else {
							      >		fatal("500 Internal Error",
							      >		      "Unexpected output from co: $_ ".
							      >		      "(Filename requested wasn't in output)");
							      >	    }
							      >	    $_ = <$fh>;
							      >	    
							      >	    if ($_ =~ /^revision\s+$rev\s.*$/) {
							      >		# As expected
							      >	    } else {
							      >		unless ($rev eq "") {
							      >		    fatal("500 Internal Error",
							      >		      "Unexpected output from co: $_ ".
							      >		      "(Revision number requested wasn't in output)")
	}						      (
	$| = 1;						      |	    }
	print "Content-type: text/plain\n\n";		      |	    return $fh
	print <RCS>;					      <
	close(RCS);					      <
}							      (
							      (
sub dodiff {						      |	sub cvsroot {
	local($fullname, $r1, $tr1, $r2, $tr2, $f) = @_;      |	    return '' if $cvstree eq $cvstreedefault;
							      >	    return "&cvsroot=" . $cvstree;
							      >	}
							      (
	if ($r1 =~ /([^:]+)(:(.+))?/) {			      |	sub display_meta {
	    $rev1 = $1;					      |	    my ($fullname, $rev) = @_;
	    $sym1 = $3;					      |	     
	}						      |	    $| = 1;
	if ($rev1 eq 'text') {				      |
	    $rev1 = $tr1;				      |	    output "Content-type: text/html\n";
	}						      |	    output "\n";
	if ($r2 =~ /([^:]+)(:(.+))?/) {			      |	    
	    $rev2 = $1;					      |	    output "<HTML><HEAD><TITLE>Meta information for $fullname
	    $sym2 = $3;					      |	    output "<BODY><H1>Meta information for $fullname rev=$rev
	}						      |	    output "<HR>\n";
	if ($rev2 eq 'text') {				      |
	    $rev2 = $tr2;				      |	# FIXME: Think of extra meta information (Filesize, cvs rstat
	}						      |
	if (!($rev1 =~ /^[\d\.]+$/) || !($rev2 =~ /^[\d\.]+$/ |	# darn, file on solaris can't work on a file on standard inpu
	    &fatal("404 Not Found",			      |	$ignore = <<'EOM'; 
		    "Malformed query \"$ENV{'QUERY_STRING'}\" |	    my $fh = get_rev($fullname, $rev);
	}						      |	    output "\nFile information:\n";
							      >	    output "<PRE>";
							      >	    open (FILECMD, "|$metaprogs{FILE}");
							      >	    print FILECMD <$fh>;
							      >	    close FILECMD;
							      >	    output "</PRE>";
							      >	EOM
							      >
							      >	    my $fh = get_rev($fullname, $rev);
							      >	    output "\nCVS/RCS information:\n";
							      >	    output "<PRE>";
							      >	    open (IDENT, "|$metaprogs{IDENT}");
							      >	    print IDENT <$fh>;
							      >	    close IDENT;
							      >	    output "</PRE>";
							      >	    
							      >	    $fh = get_rev($fullname, $rev);
							      >	    output "SCCS information:\n";
							      >	    output "<PRE>";
							      >	    open (WHAT, "|$metaprogs{WHAT} | $metaprogs{SORT}");
							      >	    print WHAT <$fh>;
							      >	    close WHAT;
							      >	    output "</PRE>";
							      >
							      >	    $fh = get_rev($fullname, $rev);
							      >	    output "LDD information:\n";
							      >	    output "<PRE>";
							      >	    open (LDD, "|$metaprogs{LDD}");
							      >	    print LDD <$fh>;
							      >	    close LDD;
							      >	    output "</PRE>";
							      >
							      >	    output "<HR>\n";
							      >	    close $fh;
							      >	}  
							      >
							      >	sub display_diff_between_revisions {
							      >	    my ($qs) = @_;
							      >
							      >	    my $r1 = $req->param('r1');
							      >	    my $r2 = $req->param('r2');
							      >	    my $tr1 = $req->param('tr1');
							      >	    my $tr2 = $req->param('tr2');
							      >
							      >	    my ($rev1, $rev2);
							      >	    my ($sym1, $sym2);
							      >	    my ($tmp1, $tmp2);
							      >	    my ($diffname, $difftype);
							      >	    my ($f1, $f2);
							      >
							      >	    if ($r1 =~ /([^:]+)(:(.+))?/) {
							      >		$rev1 = $1;
							      >		$sym1 = $3;
							      >	    }
							      >	    if ($rev1 eq 'text') {
							      >		$rev1 = $tr1;
							      >	    }
							      >	    if ($r2 =~ /([^:]+)(:(.+))?/) {
							      >		$rev2 = $1;
							      >		$sym2 = $3;
							      >	    }
							      >	    if ($rev2 eq 'text') {
							      >		$rev2 = $tr2;
							      >	    }
							      >
							      >	    do_log "REV1=$rev1, REV2=$rev2\n";
							      >
							      >	    if (!($rev1 =~ /^[\d\.]+$/) || !($rev2 =~ /^[\d\.]+$/)) {
							      >		fatal("404 Not Found",
							      >		       "Malformed query \"$ENV{'QUERY_STRING'}\"");
							      >	    }
#							      (
# rev1 and rev2 are now both numeric revisions.		      (
# Thus we do a DWIM here and swap them if rev1 is after rev2. (
# XXX should we warn about the fact that we do this?	      (
	if (&revcmp($rev1,$rev2) > 0) {			      |	   if (revcmp($rev1,$rev2) > 0) {
	    ($tmp1, $tmp2) = ($rev1, $sym1);		      |		($tmp1, $tmp2) = ($rev1, $sym1);
	    ($rev1, $sym1) = ($rev2, $sym2);		      |		($rev1, $sym1) = ($rev2, $sym2);
	    ($rev2, $sym2) = ($tmp1, $tmp2);		      |		($rev2, $sym2) = ($tmp1, $tmp2);
	}						      |	    }
#							      |
#	XXX Putting '-p' here is a personal preference	      |	    my %diffsets = ("c" => ["Context Diff", '-p -c'],
	if ($f eq 'c') {				      |			    "s" => ["Side by Side", '--side-by-side -
	    $difftype = '-p -c';			      |			    "u" => ["Unified Diff", '-p -u']);
	    $diffname = "Context diff";			      |	    $difftype = "c";
	} elsif ($f eq 's') {				      |	    if ($req->param('f')) {
	    $difftype = '--side-by-side --width=164';	      |		$difftype = $req->param('f');
	    $diffname = "Side by Side";			      |	    }
	} else {					      |
	    $difftype = '-p -u';			      |	    $diffname = $diffsets{$difftype}[0];
	    $diffname = "Unidiff";			      |	    my $diffopts = $diffsets{$difftype}[1];
	}						      |
							      >	    do_log("DIFFTYPE = $difftype, DIFFNAME= $diffname, DIFFOP
							      >
# XXX should this just be text/plain			      (
# or should it have an HTML header and then a <pre>	      (
	print "Content-type: text/plain\n\n";		      |	    output "Content-type: text/plain\n\n";
	open(RCSDIFF, "rcsdiff $difftype -r$rev1 -r$rev2 '$fu |	    my $cmd = "rcsdiff $diffopts -r$rev1 -r$rev2 '$fullname' 
	    &fail("500 Internal Error", "Couldn't rcsdiff: $! |	    do_log($cmd);
							      >	    open(RCSDIFF, $cmd) ||
							      >			fail("500 Internal Error", "$cmd - Couldn't r
#							      (
#============================================================ (
#RCS file: /home/ncvs/src/sys/netinet/tcp_output.c,v	      (
#retrieving revision 1.16				      (
#retrieving revision 1.17				      (
#diff -c -r1.16 -r1.17					      (
#*** /home/ncvs/src/sys/netinet/tcp_output.c     1995/11/03 2 (
#--- /home/ncvs/src/sys/netinet/tcp_output.c     1995/12/05 1 (
#							      (
# Ideas:						      (
# - nuke the stderr output if it's what we expect it to be    (
# - Add "no differences found" if the diff command supplied n (
#							      (
#*** src/sys/netinet/tcp_output.c     1995/11/03 22:08:08     (
#--- src/sys/netinet/tcp_output.c     1995/12/05 17:46:35     (
# (bogus example, but...)				      (
#							      (
	if ($difftype eq '-u') {			      |		    if ($difftype eq '-u') {
	    $f1 = '---';				      |			$f1 = '---';
	    $f2 = '\+\+\+';				      |			$f2 = '\+\+\+';
	} else {					      |		    } else {
	    $f1 = '\*\*\*';				      |			$f1 = '\*\*\*';
	    $f2 = '---';				      |			$f2 = '---';
	}						      |		    }
	while (<RCSDIFF>) {				      |		    while (<RCSDIFF>) {
	    if (m|^$f1 $cvsroot|o) {			      |			if (m|^$f1 $cvsroot|o) {
		s|$cvsroot/||o;				      |			    s|$cvsroot/||o;
		if ($sym1) {				      |			    if ($sym1) {
		    chop;				      |				chop;
		    $_ .= " " . $sym1 . "\n";		      |				$_ .= " " . $sym1 . "\n";
		}					      |			    }
	    } elsif (m|^$f2 $cvsroot|o) {		      |			} elsif (m|^$f2 $cvsroot|o) {
		s|$cvsroot/||o;				      |			    s|$cvsroot/||o;
		if ($sym2) {				      |			    if ($sym2) {
		    chop;				      |				chop;
		    $_ .= " " . $sym2 . "\n";		      |				$_ .= " " . $sym2 . "\n";
							      >			    }
		}					      (
							      >			output $_;
	    }						      (
	    print $_;					      |		    close(RCSDIFF);
	}						      |
	close(RCSDIFF);					      <
}							      (
							      (
sub dolog {						      <
	local($fullname) = @_;				      <
	local($curbranch,$symnames);	#...		      <
							      (
	print("Going to rlog '$fullname'\n") if ($verbose);   |	sub display_log {
	open(RCS, "rlog '$fullname'|") || &fatal("500 Interna |	        my ($fullname) = @_;
							      >
							      >		local($^W) = 0;
							      >		my %revsym;
							      >		my %log;
							      >		my $curbranch;
							      >		my $symnames;
							      >		my %date;
							      >		my @revorder;
							      >		my %symrev;
							      >		my %author;
							      >		my %state;
							      >		my $sel;
							      >		my $headrev;
							      >		my @branchnames;
							      >		my %branchpoint;
							      >
							      >		my $onlyonbranch; #FIXME - where does this get set?
							      >
							      >		
							      >		open(RCS, "rlog '$fullname'|") || fatal("500 Internal
						"Failed to sp (
	while (<RCS>) {					      (
	    print if ($verbose);			      |		    output if ($verbose);
	    if (/^branch:\s+([\d\.]+)/) {		      |	            if (/^branch:\s+([\d\.]+)/) {
		$curbranch = $1;			      |	                $curbranch = $1;
	    }						      |	            }
							      >
	    if ($symnames) {				      (
		if (/^\s+([^:]+):\s+([\d\.]+)/) {	      |			if (/^\s+([^:]+):\s+([\d\.]+)/) { 
		    $symrev{$1} = $2;			      (
		    if ($revsym{$2}) {			      (
			$revsym{$2} .= ", ";		      (
		    }					      (
		    $revsym{$2} .= $1;			      (
		} else {				      (
		    $symnames = 0;			      (
		}					      (
	    } elsif (/^symbolic names/) {		      (
		$symnames = 1;				      (
	    } elsif (/^-----/) {			      (
		last;					      (
	    }						      (
	}						      (
							      <
	if ($onlyonbranch = $input{'only_on_branch'}) {	      <
	    ($onlyonbranch = $symrev{$onlyonbranch}) =~ s/\.0 <
	    ($onlybranchpoint = $onlyonbranch) =~ s/\.\d+$//; <
	}						      <
							      <
# each log entry is of the form:			      (
# ----------------------------				      (
# revision 3.7.1.1					      (
# date: 1995/11/29 22:15:52;  author: fenner;  state: Exp;  l (
# log info						      (
# ----------------------------				      (
							      >		my $yr;
							      >		my $rev;
	logentry:					      (
	while (!/^=========/) {				      (
	    $_ = <RCS>;					      (
	    last logentry if (!defined($_));	# EOF	      |	            last logentry if (!defined($_));    # EOF
	    print "R:", $_ if ($verbose);		      |		    output "R:", $_ if ($verbose);
	    if (/^revision ([\d\.]+)/) {		      (
		$rev = $1;				      (
	    } elsif (/^========/ || /^----------------------- (
		next logentry;				      (
	    } else {					      (
		# The rlog output is syntactically ambiguous. |	                # The rlog output is syntactically ambiguous.
		# have guessed wrong about where the end of t |	                # have guessed wrong about where the end of t
		# message was.				      |	                # message was.
		# Since this is likely to happen when people  |	                # Since this is likely to happen when people 
		# in their commit messages, don't even bother |	                # in their commit messages, don't even bother
		# these lines since we don't know what revisi |	                # these lines since we don't know what revisi
		# any more.				      |	                # any more.
		next logentry;				      |	                next logentry;
#		&fatal("500 Internal Error","Error parsing RC |	#		fatal("500 Internal Error","Error parsing RCS
	    }						      (
	    $_ = <RCS>;					      (
	    print "D:", $_ if ($verbose);		      |		    output "D:", $_ if ($verbose);
	    if (m|^date:\s+(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\ |		    if (m|^date:\s+(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\
		$yr = $1;				      (
		# damn 2-digit year routines		      (
		if ($yr > 100) {			      (
		    $yr -= 1900;			      (
		}					      (
		$date{$rev} = &timelocal($6,$5,$4,$3,$2 - 1,$ |			$date{$rev} = timelocal($6,$5,$4,$3,$2 - 1,$y
		$author{$rev} = $7;			      (
		$state{$rev} = $8;			      |	                $state{$rev} = $8;
							      >
	    } else {					      (
		&fatal("500 Internal Error", "Error parsing R |			fatal("500 Internal Error", "Error parsing RC
	    }						      (
	    line:					      (
	    while (<RCS>) {				      (
		print "L:", $_ if ($verbose);		      |			output "L:", $_ if ($verbose);
		next line if (/^branches:\s/);		      (
		last line if (/^----------------------------$ (
		$log{$rev} .= $_;			      (
	    }						      (
	    print "E:", $_ if ($verbose);		      |		    output "E:", $_ if ($verbose);
	}						      (
	close(RCS);					      (
	print "Done reading RCS file\n" if ($verbose);	      |		output "Done reading RCS file\n" if ($verbose);
#							      (
# Sort the revisions into commit-date order.		      (
	@revorder = sort {$date{$b} <=> $date{$a}} keys %date (
	print "Done sorting revisions\n" if ($verbose);	      |		output "Done sorting revisions\n" if ($verbose);
#							      (
# HEAD is an artificial tag which is simply the highest tag n (
# branch, unless there is a branch tag in the RCS file in whi (
# highest revision on that branch.  Find it by looking throug (
# is the first commit listed on the appropriate branch.	      (
	$headrev = $curbranch || "1";			      |	        $headrev = $curbranch || "1";
							      >		my $i;
	revision:					      (
	for ($i = 0; $i <= $#revorder; $i++) {		      |	        for ($i = 0; $i <= $#revorder; $i++) {
	    if ($revorder[$i] =~ /^(\S*)\.\d+$/ && $headrev e |	            if ($revorder[$i] =~ /^(\S*)\.\d+$/ && $headrev e
		if ($revsym{$revorder[$i]}) {		      |	                if ($revsym{$revorder[$i]}) {
		    $revsym{$revorder[$i]} .= ", ";	      |	                    $revsym{$revorder[$i]} .= ", ";
		}					      |	                }
		$revsym{$revorder[$i]} .= "HEAD";	      |	                $revsym{$revorder[$i]} .= "HEAD";
		$symrev{"HEAD"} = $revorder[$i];	      |	                $symrev{"HEAD"} = $revorder[$i];
		last revision;				      |	                last revision;
	    }						      |	            }
	}						      |	        }
	print "Done finding HEAD\n" if ($verbose);	      |		output "Done finding HEAD\n" if ($verbose);
#							      (
# Now that we know all of the revision numbers, we can associ (
# absolute revision numbers with all of the symbolic names, a (
# pass them to the form so that the same association doesn't  (
# to be built then.					      (
#							      (
# should make this a case-insensitive sort		      (
							      >		my ($head, $branch, $regex);
	foreach (sort keys %symrev) {			      (
	    $rev = $symrev{$_};				      (
	    if ($rev =~ /^(\d+(\.\d+)+)\.0\.(\d+)$/) {	      (
		push(@branchnames, $_);			      |	                push(@branchnames, $_);
		#					      (
		# A revision number of A.B.0.D really transla (
		# "the highest current revision on branch A.B (
		#					      (
		# If there is no branch A.B.D, then it transl (
		# the head A.B .			      (
		#					      |
		$head = $1;				      (
		$branch = $3;				      (
		$regex = $head . "." . $branch;		      (
		$regex =~ s/\./\./g;			      (
		#             <				      (
		#           \____/			      (
		$rev = $head;				      (
							      |			
		revision:				      (
							      >			my $r;
							      >			my $rev;
		foreach $r (@revorder) {		      (
		    if ($r =~ /^${regex}/) {		      (
			$rev = $head . "." . $branch;	      (
			last revision;			      (
		    }					      (
		}					      (
		$revsym{$rev} .= ", " if ($revsym{$rev});     (
		$revsym{$rev} .= $_;			      (
		if ($rev ne $head) {			      |	                if ($rev ne $head) {
		    $branchpoint{$head} .= ", " if ($branchpo |	                    $branchpoint{$head} .= ", " if ($branchpo
		    $branchpoint{$head} .= $_;		      |	                    $branchpoint{$head} .= $_;
		}					      |	                }
	    }						      (
	    $sel .= "<OPTION VALUE=\"${rev}:${_}\">$_\n";     (
	}						      (
	print "Done associating revisions with branches\n" if |		output "Done associating revisions with branches\n" i
        print &html_header("CVS log for $where");	      |	        output html_header("CVS log for $where");
	($upwhere = $where) =~ s|(Attic/)?[^/]+$||;	      |
	print "Up to ", &link($upwhere,$scriptname . "/" . $u |	        my $upwhere;
	print "<BR>\n";					      |
	print "<A HREF=\"#diff\">Request diff between arbitra |	        ($upwhere = $where) =~ s|(Attic/)?[^/]+$||;
	print "<HR NOSHADE>\n";				      |	        output "Up to ", htlink($upwhere,$scriptname . "/" . 
	if ($curbranch) {				      |	        output "<BR>\n";
	    print "Default branch is ";			      |	        output "<A HREF=\"#diff\">Request diff between arbitr
	    print ($revsym{$curbranch} || $curbranch);	      |	        output "<HR NOSHADE>\n";
	} else {					      |	        if ($curbranch) {
	    print "No default branch";			      |	            output "Default branch is ";
	}						      |	            output ($revsym{$curbranch} || $curbranch);
	print "<BR><HR NOSHADE>\n";			      |	        } else {
							      >	            output "No default branch";
							      >	        }
							      >	        output "<BR><HR NOSHADE>\n";
							      >
# The other possible U.I. I can see is to have each revision  (
# and have the first one you click do ?r1=foo		      (
# and since there's no r2 it keeps going & the next one you c (
# adds ?r2=foo and performs the query.			      (
# I suppose there's no reason we can't try both and see which (
# people prefer...					      (
							      (
	for ($i = 0; $i <= $#revorder; $i++) {		      |	my @prevrev;
							      >	my $br;
							      >	my $prev;
							      >	my ($tmp1, $tmp2);
							      >	my (@tmp1, @tmp2);
							      >	my $sym;
							      >	my %nameprinted;
							      >	my %branchpoint; #FIXME - is this in the right place?
							      >	my $onlybranchpoint; #FIXME - where does this get set?
							      >
							      >	       for ($i = 0; $i <= $#revorder; $i++) {
	    $_ = $revorder[$i];				      (
	    ($br = $_) =~ s/\.\d+$//;			      |	            ($br = $_) =~ s/\.\d+$//;
	    next if ($onlyonbranch && $br ne $onlyonbranch && |	            next if ($onlyonbranch && $br ne $onlyonbranch &&
					    $_ ne $onlybranch |	                                            $_ ne $onlybranch
	    print "<a NAME=\"rev$_\"></a>";		      |	            output "<a NAME=\"rev$_\"></a>";
	    foreach $sym (split(", ", $revsym{$_})) {	      |	            foreach $sym (split(", ", $revsym{$_})) {
		print "<a NAME=\"$sym\"></a>";		      |	                output "<a NAME=\"$sym\"></a>";
	    }						      |	            }
	    if ($revsym{$br} && !$nameprinted{$br}) {	      |	            if ($revsym{$br} && !$nameprinted{$br}) {
		foreach $sym (split(", ", $revsym{$br})) {    |	                foreach $sym (split(", ", $revsym{$br})) {
		    print "<a NAME=\"$sym\"></a>";	      |	                    output "<a NAME=\"$sym\"></a>";
		}					      |	                }
		$nameprinted{$br}++;			      |	                $nameprinted{$br}++;
	    }						      |	            }
	    print "\n";					      |	            output "\n";
	    print "<A HREF=\"$scriptwhere?rev=$_" . 	      |
		&cvsroot . "\"><b>$_</b></A>";		      |		    output "<b>$_</b>";
							      >		    output "<A HREF=\"$scriptwhere?rev=$_\">auto</A>,
							      >		    output "<A HREF=\"$scriptwhere?rev=$_&content-typ
							      >		    output "<A HREF=\"$scriptwhere?rev=$_&content-typ
							      >
							      >
	    if (/^1\.1\.1\.\d+$/) {			      (
		print " <i>(vendor branch)</i>";	      |			output " <i>(vendor branch)</i>";
	    }						      (
	    print " <i>" . &ctime($date{$_}) . " UTC</i> by " |		    output " <i>" . ctime($date{$_}) . " UTC</i> by "
	    print "<i>" . $author{$_} . "</i>\n";	      |		    output "<i>" . $author{$_} . "</i>\n";
							      >		    output " <A HREF=\"$scriptwhere?rev=$_&meta=1\"><
							      >		    output " <A HREF=\"$cvswebedit_url/$where?edit=st
							      >
	    if ($revsym{$_}) {				      (
		print "<BR>CVS Tags: <b>$revsym{$_}</b>";     |			output "<BR>CVS Tags: <b>$revsym{$_}</b>";
	    }						      (
	    if ($revsym{$br})  {			      (
		if ($revsym{$_}) {			      (
		    print "; ";				      |			    output "; ";
		} else {				      (
		    print "<BR>";			      |			    output "<BR>";
		}					      (
		print "Branch: <b>$revsym{$br}</b>\n";	      |			output "Branch: <b>$revsym{$br}</b>";
	    }						      <
	    if ($branchpoint{$_}) {			      <
		if ($revsym{$br} || $revsym{$_}) {	      <
		    print "; ";				      <
		} else {				      <
		    print "<BR>";			      <
		}					      <
		print "Branch point for: <b>$branchpoint{$_}< <
	    }						      (
							      >	            if ($branchpoint{$_}) {
							      >	                if ($revsym{$br} || $revsym{$_}) {
							      >	                    output "; ";
							      >	                } else {
							      >	                    output "<BR>";
							      >	                }
							      >	                output "Branch point for: <b>$branchpoint{$_}
							      >	            }
	    # Find the previous revision on this branch.      (
	    @prevrev = split(/\./, $_);			      (
	    if (--$prevrev[$#prevrev] == 0) {		      (
		# If it was X.Y.Z.1, just make it X.Y	      (
		if ($#prevrev > 1) {			      (
		    pop(@prevrev);			      (
		    pop(@prevrev);			      (
		} else {				      (
		    # It was rev 1.1 (XXX does CVS use revisi (
		    # greater than 1.x?)		      (
		    if ($prevrev[0] != 1) {		      (
			print "<i>* I can't figure out the pr |				output "<i>* I can't figure out the p
		    }					      (
		}					      (
	    }						      (
	    if ($prevrev[$#prevrev] != 0) {		      (
		$prev = join(".", @prevrev);		      (
		print "<BR><A HREF=\"${scriptwhere}.diff?r1=$ |			output "<BR><A HREF=\"$scriptwhere?r1=$prev";
		print "&r2=$_" . &cvsroot . "\">Diffs to $pre |			output "&r2=$_\">Diffs to $prev</A>\n";
		#					      (
		# Plus, if it's on a branch, and it's not a v (
		# offer to diff with the immediately-precedin (
		# is not the previous revision as calculated  (
		# and if it is on the HEAD (or at least on a  (
		# (e.g. change gets committed and then brough (
		# over to -stable)			      (
		if (!/^1\.1\.1\.\d+$/ && ($i != $#revorder) & (
					($prev ne $revorder[$ (
		    @tmp1 = split(/\./, $revorder[$i+1]);     (
		    @tmp2 = split(/\./, $_);		      (
		    if ($#tmp1 < $#tmp2) {		      (
			print "; <A HREF=\"${scriptwhere}.dif |				output "; <A HREF=\"$scriptwhere?r1=$
			print "&r2=$_" . &cvsroot .	      |				output "&r2=$_\">Diffs to $revorder[$
                            "\">Diffs to $revorder[$i+1]</A>\ <
		    }					      (
		}					      (
	    }						      (
	    if ($state{$_} eq "dead") {			      |	            if ($state{$_} eq "dead") {
		print "<BR><B><I>FILE REMOVED</I></B>\n";     |	                output "<BR><B><I>FILE REMOVED</I></B>\n";
	    }						      |	            }
	    print "<PRE>\n";				      |	            output "<PRE>\n";
	    print &htmlify($log{$_}, 1);		      |	            output htmlify($log{$_}, 1);
	    print "</PRE><HR NOSHADE>\n";		      |	            output "</PRE><HR NOSHADE>\n";
	}						      |
	print "<A NAME=diff>\n";			      |		}
	print "This form allows you to request diff's between |	display_diff_footer($sel, \@revorder, \@branchnames);
	print "revisions of a file.  You may select a symboli <
	print "name using the selection box or you may type i <
	print "name using the type-in text box.\n";	      <
	print "</A><P>\n";				      <
	print "<FORM METHOD=\"GET\" ACTION=\"${scriptwhere}.d <
        print "<INPUT TYPE=HIDDEN NAME=\"cvsroot\" VALUE=\"$c <
             if &cvsroot;				      <
	print "Diffs between \n";			      <
	print "<SELECT NAME=\"r1\">\n";			      <
	print "<OPTION VALUE=\"text\" SELECTED>Use Text Field <
	print $sel;					      <
	print "</SELECT>\n";				      <
	print "<INPUT TYPE=\"TEXT\" NAME=\"tr1\" VALUE=\"$rev <
	print " and \n";				      <
	print "<SELECT NAME=\"r2\">\n";			      <
	print "<OPTION VALUE=\"text\" SELECTED>Use Text Field <
	print $sel;					      <
	print "</SELECT>\n";				      <
	print "<INPUT TYPE=\"TEXT\" NAME=\"tr2\" VALUE=\"$rev <
	print "<BR><INPUT TYPE=RADIO NAME=\"f\" VALUE=u CHECK <
	print "<INPUT TYPE=RADIO NAME=\"f\" VALUE=c>Context d <
	print "<INPUT TYPE=RADIO NAME=\"f\" VALUE=s>Side-by-S <
	print "<INPUT TYPE=SUBMIT VALUE=\"Get Diffs\">\n";    <
	print "</FORM>\n";				      <
	print "<HR noshade>\n";				      <
	print "<A name=branch>\n";			      <
	print "You may select to see revision information fro <
	print "a single branch.\n";			      <
	print "</A><P>\n";				      <
	print "<FORM METHOD=\"GET\" ACTION=\"$scriptwhere\">\ <
        print qq{<input type=hidden name=cvsroot value=$cvstr <
             if &cvsroot;				      <
	print "Branch: \n";				      <
	print "<SELECT NAME=\"only_on_branch\">\n";	      <
	print "<OPTION VALUE=\"\"";			      <
	print " SELECTED" if ($input{"only_on_branch"} eq "") <
	print ">Show all branches\n";			      <
	foreach (sort @branchnames) {			      <
		print "<OPTION";			      <
		print " SELECTED" if ($input{"only_on_branch" <
		print ">${_}\n";			      <
	}						      <
	print "</SELECT>\n";				      <
	print "<INPUT TYPE=SUBMIT VALUE=\"View Branch\">\n";  <
	print "</FORM>\n";				      <
        print &html_footer;				      <
	print "</BODY></HTML>\n";			      <
}							      (
							      (
sub cvsroot {						      |
    return '' if $cvstree eq $cvstreedefault;		      |	sub display_diff_footer ($$$) {
    return "&cvsroot=" . $cvstree;			      |	    local ($^W) = 0; # disable warnings
							      >	    my ($sel, $refrevorder, $refbranchnames) = @_;
							      >	    my (@revorder) = @$refrevorder;
							      >	    my (@branchnames) = @$refbranchnames;
							      >
							      >	    output "<A NAME=diff>\n";
							      >	    output "This form allows you to request diff's between an
							      >	    output "revisions of a file.  You may select a symbolic r
							      >	    output "name using the selection box or you may type in a
							      >	    output "name using the type-in text box.\n";
							      >	    output "</A><P>\n";
							      >	    output "<FORM METHOD=\"GET\" ACTION=\"${scriptwhere}.diff
							      >	    output "<INPUT TYPE=HIDDEN NAME=\"cvsroot\" VALUE=\"$cvst
							      >		if cvsroot();
							      >
							      >	    output "Diffs between \n";
							      >	    output "<SELECT NAME=\"r1\">\n";
							      >	    output "<OPTION VALUE=\"text\" SELECTED>Use Text Field\n"
							      >	    output $sel;
							      >	    output "</SELECT>\n";
							      >	    output "<INPUT TYPE=\"TEXT\" NAME=\"tr1\" VALUE=\"$revord
							      >	    output " and \n";
							      >	    output "<SELECT NAME=\"r2\">\n";
							      >	    output "<OPTION VALUE=\"text\" SELECTED>Use Text Field\n"
							      >	    output $sel;
							      >	    output "</SELECT>\n";
							      >	    output "<INPUT TYPE=\"TEXT\" NAME=\"tr2\" VALUE=\"$revord
							      >	    output "<BR><INPUT TYPE=RADIO NAME=\"f\" VALUE=u CHECKED>
							      >	    output "<INPUT TYPE=RADIO NAME=\"f\" VALUE=c>Context diff
							      >	    output "<INPUT TYPE=RADIO NAME=\"f\" VALUE=s>Side-by-Side
							      >	    output "<INPUT TYPE=SUBMIT VALUE=\"Get Diffs\">\n";
							      >	    output "</FORM>\n";
							      >	    output "<HR noshade>\n";
							      >	    output "<A name=branch>\n";
							      >	    output "You may select to see revision information from o
							      >	    output "a single branch.\n";
							      >	    output "</A><P>\n";
							      >	    output "<FORM METHOD=\"GET\" ACTION=\"$scriptwhere\">\n";
							      >	    output qq{<input type=hidden name=cvsroot value=$cvstree>
							      >	    output "Branch: \n";
							      >	    output "<SELECT NAME=\"only_on_branch\">\n";
							      >	    output "<OPTION VALUE=\"\"";
							      >	    output " SELECTED" if ($req->param("only_on_branch") eq "
							      >	    output ">Show all branches\n";
							      >	    foreach (sort @branchnames) {
							      >		output "<OPTION";
							      >		output " SELECTED" if ($req->param("only_on_branch") 
							      >		output ">${_}\n";
							      >	    }
							      >	    output "</SELECT>\n";
							      >	    output "<INPUT TYPE=SUBMIT VALUE=\"View Branch\">\n";
							      >	    output "</FORM>\n";
							      >	    output html_footer();
							      >	    output "</BODY></HTML>\n";
}							      (
							      >
