#!/usr/bonsaitools/bin/perl -w # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Netscape Public # License Version 1.1 (the "License"); you may not use this file # except in compliance with the License. You may obtain a copy of # the License at http://www.mozilla.org/NPL/ # # Software distributed under the License is distributed on an "AS # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or # implied. See the License for the specific language governing # rights and limitations under the License. # # The Original Code is the Bonsai CVS tool. # # The Initial Developer of the Original Code is Netscape Communications # Corporation. Portions created by Netscape are # Copyright (C) 1998 Netscape Communications Corporation. All # Rights Reserved. # # Contributor(s): # cvslog.cgi -- cvslog with logs as popups and allowing html in comments. # # Created: Steve Lamm , 31-Mar-98. # # Arguments (passed via GET or POST): # file - path to file name (e.g. ns/cmd/xfe/Makefile) # root - cvs root (e.g. /warp/webroot) # rev - revision (default is the latest version) # mark - highlight a revision # author - filter based on author # use diagnostics; use strict; # Shut up misguided -w warnings about "used only once". "use vars" just # doesn't work for me. sub sillyness { my $zz; $zz = $::CVS_ROOT; $zz = $::head_revision; $zz = $::revision_ctime; $zz = $::revision_log; } require 'CGI.pl'; require 'cvsblame.pl'; use SourceChecker; # Some Globals # $| = 1; print "Content-Type:text/html\n\n"; my @src_roots = getRepositoryList(); # Handle the "file" argument # my $filename = ''; $filename = $::FORM{'file'} if defined($::FORM{'file'}); if ($filename eq '') { &print_usage; exit; } my ($file_head, $file_tail) = $filename =~ m@(.*/)?(.+)@; # Handle the "rev" argument # $::opt_rev = ""; $::opt_rev = $::FORM{'rev'} if defined($::FORM{'rev'} && $::FORM{'rev'} ne 'HEAD'); my $browse_revtag = 'HEAD'; $browse_revtag = $::opt_rev if ($::opt_rev =~ /[A-Za-z]/); my $revision = ''; # Handle the "root" argument # my $root = $::FORM{'root'}; if (defined $root && $root ne '') { $root =~ s|/$||; validateRepository($root); if (-d $root) { unshift(@src_roots, $root); } else { &print_top; print "Error: Root, $root, is not a directory.

\n"; print "\n"; &print_bottom; exit; } } # Find the rcs file # my $rcs_filename; foreach (@src_roots) { $root = $_; $rcs_filename = "$root/$filename,v"; $rcs_filename = Fix_BonsaiLink($rcs_filename); goto found_file if -r $rcs_filename; $rcs_filename = "$root/${file_head}Attic/$file_tail,v"; goto found_file if -r $rcs_filename; } # File not found &print_top; print "Rcs file, $filename, does not exist.

\n"; print "\n"; &print_bottom; exit; found_file: my $rcs_path; ($rcs_path) = $rcs_filename =~ m@$root/(.*)/.+?,v@; # Parse the rcs file ($::opt_rev is passed as a global) # $revision = &parse_cvs_file($rcs_filename); my $file_rev = $revision; # Handle the "mark" argument # my %mark; my $mark_arg = ''; $mark_arg = $::FORM{'mark'} if defined($::FORM{'mark'}); foreach my $rev (split(',',$mark_arg)) { $mark{$rev} = 1; } # Handle the "author" argument # my %use_author; my $author_arg = ''; $author_arg = $::FORM{'author'} if defined($::FORM{'author'}); foreach my $author (split(',',$author_arg)) { $use_author{$author} = 1; } # Handle the "sort" argument my $opt_sort = ''; $opt_sort = $::FORM{'sort'} if defined $::FORM{'sort'}; # Start printing out the page # &print_top; print Param('bannerhtml', 1); # Print link at top for directory browsing # print q(
CVS Log
); my $link_path; my $lxr_path; foreach my $path (split('/',$rcs_path)) { $link_path .= url_encode2($path).'/'; $lxr_path = Fix_LxrLink($link_path); print "$path/ "; } $lxr_path = Fix_LxrLink("$link_path$file_tail"); print "$file_tail "; print " ("; print "$browse_revtag:" unless $browse_revtag eq 'HEAD'; print $revision if $revision; print ")"; print qq(
lxr Browse the source code as hypertext.
diff Compare any two version.
blame  Annotate the author of each line.
); #&print_useful_links($filename); # Create a table with header links to sort by column. # my $table_tag = ""; my $table_header_tag = ""; if ($opt_sort eq 'author') { $table_header_tag .= "
RevAuthorDateLog"; } else { $table_header_tag .= "RevAuthorDateLog"; } $table_header_tag = &url_encode3($table_header_tag); print "$table_tag$table_header_tag"; # Print each line of the revision, preceded by its annotation. # my $start_rev; if ($browse_revtag eq 'HEAD') { $start_rev = $::head_revision; # $::head_revision is a global from cvsblame.pl } else { $start_rev = map_tag_to_revision($browse_revtag); } my $row_count = 0; my $max_rev_length = length($start_rev); my $max_author_length = 8; my @revisions = ($start_rev, ancestor_revisions($start_rev)); @revisions = sort by_author @revisions if $opt_sort eq 'author'; #@revisions = sort by_author @revisions if $opt_sort eq 'date' && $rev eq 'all'; my $bgcolor; foreach $revision (@revisions) { my $author = $::revision_author{$revision}; next unless $author_arg eq '' || $use_author{$author}; my $log = $::revision_log{$revision}; $log =~ s/&/&/g; $log =~ s//>/g; $log = MarkUpText($log); $log =~ s/\n|\r|\r\n/
/g; if (!defined $bgcolor || $bgcolor eq '') { #$bgcolor = ' BGCOLOR="#EEEEEE"';# My browser translates this to white. $bgcolor = ' BGCOLOR="#E7E7E7"'; # Pick a grey that shows up on 8-bit. } else { $bgcolor = ''; } my $output = ''; $row_count++; if ($row_count > 20) { $output .= "
\n$table_tag"; $row_count = 0; } $output .= "" .""; my $anchor = "cvsview2.cgi"; if (defined($::prev_revision{$revision})) { $anchor .= "?diff_mode=context&whitespace_mode=show&file=$file_tail" ."&root=$root&subdir=$rcs_path&command=DIFF_FRAMESET" ."&rev1=$::prev_revision{$revision}&rev2=$revision"; } else { $anchor .= "?files=$file_tail" ."&root=$root&subdir=$rcs_path\&command=DIRECTORY\&rev2=$revision"; $anchor .= "&branch=$browse_revtag" unless $browse_revtag eq 'HEAD'; } $anchor = &url_encode3($anchor); $output .= "$revision" .' ' x ($max_rev_length - length($revision)).''; $output .= "".$author .' ' x ($max_author_length - length($author)).''; my $rev_time = $::revision_ctime{$revision}; # $rev_time =~ s/(19\d\d) (.\d:\d\d)/$1
$2<\/FONT>/; # jwz: print the date the way "ls" does. # # What ls does is actually: print "Mmm DD HH:MM" unless the file is # more than six months old, or more than 1 hour in the future, in # which case, print "Mmm DD YYYY". # # What the following does is: "Mmm DD HH:MM" unless the year is not # the current year; else print "Mmm DD YYYY". # # If we had $rev_time as an actual time_t instead of as a string, # it would be easy to do the "ls" thing (see the code I wrote for # this in "lxr/source"). -jwz, 15-Jun-98. # { my $current_time = time; my @t = gmtime($current_time); my ($csec, $cmin, $chour, $cmday, $cmon, $cyear) = @t; $cyear += 1900; $_ = $rev_time; my ($rday, $rmon, $ryear, $rhour, $rmin) = m/([0-9]+) ([A-Z][a-z]+) ([0-9][0-9]+) +([0-9]+):([0-9]+)/; $rmon =~ s/^(...).*$/$1/; if (!$rday) { # parse error -- be annoying so somebody complains. $rev_time = "\"$rev_time\""; } elsif ($cyear ne $ryear) { $rev_time = sprintf("%s %2d %04d", $rmon, $rday, $ryear); } else { $rev_time = sprintf("%s %2d %02d:%02d", $rmon, $rday, $rhour, $rmin); } $rev_time = "$rev_time"; } $output .= "$rev_time"; $output .= " $log"; $output .= "\n"; print $output; } print ""; &print_bottom; ## END of main script sub by_revision { my (@a_parts) = split(/\./,$a); my (@b_parts) = split(/\./,$b); while(1) { my ($aa) = shift @a_parts; my ($bb) = shift @b_parts; return 1 if $aa eq ''; return -1 if $bb eq ''; return $bb <=> $aa if $aa ne $bb; } } sub by_author { my ($a_author) = $::revision_author{$a}; my ($b_author) = $::revision_author{$b}; return $a_author cmp $b_author if $a_author ne $b_author; return by_revision; } sub revision_pad { my ($revision) = @_; return ' ' x ($max_rev_length - length($revision)); } sub sprint_author { my ($revision) = @_; my ($author) = $::revision_author{$revision}; return } sub print_top { my ($title_text) = "for $file_tail ("; $title_text .= "$browse_revtag:" unless $browse_revtag eq 'HEAD'; $title_text .= $revision if $revision; $title_text .= ")"; $title_text =~ s/\(\)//; print <<__TOP__; CVS Log $title_text __TOP__ } # print_top sub print_usage { my ($linenum_message) = ''; my ($new_linenum, $src_roots_list); my ($title_text) = "Usage"; $src_roots_list = join('
', @src_roots); print <<__USAGE__; CVS Log $title_text

CVS Log Usage

Add parameters to the query string to view a file.

Param Default Example Description
file -- ns/cmd/Makefile Path to file name
root $src_roots_list /warp/webroot CVS root
rev HEAD 1.3
ACTRA_branch
Revision
author -- slamm,mtoy Filter out these authors
#<rev_number> -- #1.2 Jump to a revision

Examples:
  cvslog.cgi?file=ns/cmd/Makefile
  cvslog.cgi?file=ns/cmd/xfe/mozilla.c&rev=Dogbert4xEscalation_BRANCH
  cvslog.cgi?file=projects/bonsai/cvslog.cgi&root=/warp/webroot
  cvslog.cgi?file=ns/cmd/xfe/dialogs.c#1.19

You may also begin a query with the CVS Query Form.

__USAGE__ &print_bottom; } # sub print_usage sub print_bottom { my $maintainer = Param('maintainer'); print <<__BOTTOM__;
Page configuration and help. Mail feedback to <$maintainer>. __BOTTOM__ } # print_bottom sub print_useful_links { my ($path) = @_; my ($dir, $file) = $path =~ m@(.*/)?(.+)@; $dir =~ s@/$@@; my $diff_base = "cvsview2.cgi"; my $blame_base = "cvsblame.cgi"; my $lxr_path = $path; my $lxr_link = Fix_LxrLink($lxr_path); my $diff_link = "$diff_base?command=DIRECTORY\&subdir=$dir\&files=$file"; my $blame_link = "$blame_base?root=$::CVS_ROOT\&file=$path"; print "
lxr: browse the source code as hypertext.
diff: compare any two versions.
blame: annotate the author of each line.
"; }