#!/usr/bin/perl -s # # cvsweb - a CGI interface to the CVS tree. # # Written by Bill Fenner on his own time. # Insert BSD copyright here. # #HTTP_USER_AGENT: Mozilla/1.1N (X11; I; SunOS 4.1.3_U1 sun4m) via proxy gateway CERN-HTTPD/3.0 libwww/2.17 #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 # require 'timelocal.pl'; require 'ctime.pl'; $hsty_base = ""; require 'cgi-style.pl'; $cvsroot = '/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. src/). 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.

If you would like to use this CGI script on your own web server and CVS tree, see the CVSWeb distribution site.

Please send any suggestions, comments, etc. to Bill Fenner <fenner@freebsd.org> "; $shortinstr = " Click on a directory to enter that directory. Click on a file to display its revision history and to get a chance to display diffs between revisions. "; $verbose = $v; ($where = $ENV{'PATH_INFO'}) =~ s|^/||; $where =~ s|/$||; $fullname = $cvsroot . '/' . $where; ($scriptname = $ENV{'SCRIPT_NAME'}) =~ s|^/?|/|; $scriptname =~ s|/$||; $scriptwhere = $scriptname . '/' . $where; $scriptwhere =~ s|/$||; if (!-d $cvsroot) { &fatal("500 Internal Error",'$CVSROOT not found!'); } if ($q = $ENV{'QUERY_STRING'}) { foreach (split(/&/, $q)) { s/%(..)/sprintf("%c", hex($1))/ge; # unquote %-quoted if (/(\S+)=(.*)/) { $input{$1} = $2; } else { $input{$_}++; } } } if (-d $fullname) { opendir(DIR, $fullname) || &fatal("404 Not Found","$where: $!"); @dir = readdir(DIR); closedir(DIR); if ($where eq '') { print &html_header("FreeBSD CVS Repository"); print $intro; } else { print &html_header("/$where"); print $shortinstr; } print "

Current directory: /$where\n"; print "


\n"; # Using in this manner violates the HTML2.0 spec but # provides the results that I want in most browsers. Another # case of layout spooging up HTML. print "\n"; foreach (sort @dir) { if ($_ eq '.') { next; } if ($_ eq '..') { next if ($where eq ''); ($updir = $scriptwhere) =~ s|[^/]+$||; print " ", &link("Previous Directory",$updir), "
"; # print " ", # &link("Directory-wide diffs", $scriptwhere . '/*'), "
"; } elsif (-d $fullname . "/" . $_) { print " ", &link($_ . "/", $scriptwhere . '/' . $_ . '/'), "
"; } elsif (s/,v$//) { # TODO: add date/time? How about sorting? print " ", &link($_, $scriptwhere . '/' . $_), "
"; } } print "
\n"; print &html_footer; print "\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{'f'}); exit; } open(RCS, "rlog '$fullname'|") || &fatal("500 Internal Error", "Failed to spawn rlog"); while () { print if ($verbose); if ($symnames) { 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; lines: +5 -3 # log info # ---------------------------- logentry: while (!/^=========/) { $_ = ; print "R:", $_ if ($verbose); if (/^revision ([\d\.]+)/) { $rev = $1; } elsif (/^========/ || /^----------------------------$/) { next logentry; } else { &fatal("500 Internal Error","Error parsing RCS output: $_"); } $_ = ; print "D:", $_ if ($verbose); if (m|^date:\s+(\d+)/(\d+)/(\d+)\s+(\d+):(\d+):(\d+);\s+author:\s+(\S+);|) { $yr = $1; # damn 2-digit year routines if ($yr > 100) { $yr -= 1900; } $date{$rev} = &timelocal($6,$5,$4,$3,$2 - 1,$yr); $author{$rev} = $7; } else { &fatal("500 Internal Error", "Error parsing RCS output: $_"); } line: while () { print "L:", $_ if ($verbose); next line if (/^branches:\s/); last line if (/^----------------------------$/ || /^=========/); $log{$rev} .= $_; } print "E:", $_ if ($verbose); } close(RCS); print "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); # # HEAD is an artificial tag which is simply the highest tag number on the main # branch (I think!). Find it by looking through @revorder; it should at least # be near the beginning (In fact, it *should* be the first commit listed on # the main branch.) revision: for ($i = 0; $i <= $#revorder; $i++) { if ($revorder[$i] =~ /^\d+\.\d+$/) { if ($revsym{$revorder[$i]}) { $revsym{$revorder[$i]} .= ", "; } $revsym{$revorder[$i]} .= "HEAD"; $symrev{"HEAD"} = $revorder[$i]; last revision; } } print "Done finding HEAD\n" if ($verbose); # # Now that we know all of the revision numbers, we can associate # absolute revision numbers with all of the symbolic names, and # pass them to the form so that the same association doesn't have # to be built then. # # should make this a case-insensitive sort foreach (sort keys %symrev) { $rev = $symrev{$_}; if ($rev =~ /^(\d+(\.\d+)+)\.0\.(\d+)$/) { push(@branchnames, $_); # # A revision number of A.B.0.D really translates into # "the highest current revision on branch A.B.D". # # If there is no branch A.B.D, then it translates into # the head A.B . # # This is pure speculation. # $head = $1; $branch = $3; $regex = $head . "." . $branch; $regex =~ s/\./\./g; # < # \____/ $rev = $head; revision: foreach $r (@revorder) { if ($r =~ /^${regex}/) { $rev = $head . "." . $branch; last revision; } } $revsym{$rev} .= ", " if ($revsym{$rev}); $revsym{$rev} .= $_; } $sel .= "