#!/usr/bonsaitools/bin/perl -- # cvsblame.cgi -- cvsblame with logs as popups and allowing html in comments. # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Netscape Public License # Version 1.0 (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. # Created: Steve Lamm , 12-Sep-97. # Modified: Marc Byrd , 19971030. # # Arguments (passed via GET or POST): # file - path to file name (e.g. ns/cmd/xfe/Makefile) # root - cvs root (e.g. /warp/webroot) # - default includes /m/src/ and /h/rodan/cvs/repository/1.0 # rev - revision (default is the latest version) # line_nums - boolean for line numbers on/off (use 1,0). # (1,on by default) # use_html - boolean for html comments on/off (use 1,0). # (0,off by default) # sanitize - path to sanitization dictionary # (e.g. /warp2/webdoc/projects/bonsai/dictionary/sanitization.db) # mark - highlight a line # require 'lloydcgi.pl'; require 'cvsblame.pl'; require 'utils.pl'; use SourceChecker; $| = 1; # Cope with the cookie and print the header, first thing. That way, if # any errors result, they will show up for the user. print "Content-Type:text/html\n"; if ($ENV{"REQUEST_METHOD"} eq 'POST' && defined($form{'set_line'})) { # Expire the cookie 5 months from now print "Set-Cookie: line_nums=$form{'set_line'}; expires=" . toGMTString(time + 86400 * 152) . "; path=/\n"; } print "\n"; # Some Globals # @src_roots = getRepositoryList(); # Do not use layers if the client does not support them. $useLayers = 1; $user_agent = $ENV{HTTP_USER_AGENT}; if (not $user_agent =~ m@^Mozilla/4.@ or $user_agent =~ /MSIE/) { $useLayers = 0; } #if ($user_agent =~ /Win/) { # $font_tag = "
";
#} else {
#    # We don't want your stinking Windows font
    $font_tag = "
";
#}

# Init sanitiazation source checker
#
$sanitization_dictionary = $form{'sanitize'};
$opt_sanitize = defined $sanitization_dictionary;
if ( $opt_sanitize )
{
    dbmopen %SourceChecker::token_dictionary, "$sanitization_dictionary", 0664;
}

# Init byrd's 'feature' to allow html in comments
#
$opt_html_comments = &html_comments_init();


# Handle the "file" argument
#
$filename = '';
$filename = $form{'file'} if defined($form{'file'});
if ($filename eq '') 
{
    &print_usage;
    exit;
}
($file_head, $file_tail) = $filename =~ m@(.*/)?(.+)@;

# Handle the "rev" argument
#
$opt_rev = $form{'rev'} if defined($form{'rev'} && $form{'rev'} ne 'HEAD');
$browse_revtag = "HEAD";
$browse_revtag = $opt_rev if ($opt_rev =~ /[A-Za-z]/);
$revision = '';


# Handle the "root" argument
#
if (defined($root = $form{'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 # foreach (@src_roots) { $root = $_; $rcs_filename = "$root/$filename,v"; 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: ($rcs_path) = $rcs_filename =~ m@$root/(.*)/.+?,v@; CheckHidden($rcs_filename); # Parse the rcs file ($opt_rev is passed as a global) # $revision = &parse_cvs_file($rcs_filename); $file_rev = $revision; # Handle the "line_nums" argument # $opt_line_nums = 1; $opt_line_nums = 1 if $cookie_jar{'line_nums'} eq 'on'; $opt_line_nums = 0 if $form{'line_nums'} =~ /off|no|0/i; $opt_line_nums = 1 if $form{'line_nums'} =~ /on|yes|1/i; # Option to make links to included files $opt_includes = 0; $opt_includes = 1 if $form{'includes'} =~ /on|yes|1/i; $opt_includes = 1 if $opt_includes && $file_tail =~ /(.c|.h|.cpp)$/; @text = &extract_revision($revision); die "$progname: Internal consistency error" if ($#text != $#revision_map); # Handle the "mark" argument # $mark_arg = ''; $mark_arg = $form{'mark'} if defined($form{'mark'}); foreach $mark (split(',',$mark_arg)) { if (($begin, $end) = $mark =~ /(\d*)\-(\d*)/) { $begin = 1 if $begin eq ''; $end = $#text + 1 if $end eq '' || $end > $#text + 1; next if $begin >= $end; $mark_line{$begin} = 'begin'; $mark_line{$end} = 'end'; } else { $mark_line{$mark} = 'single'; } } # Start printing out the page # &print_top; # Print Header # # Print link at top for directory browsing # print q(
Blame Annotated Source
); foreach $path (split('/',$rcs_path)) { $link_path .= url_encode2($path).'/' if $path ne 'mozilla'; print "$path/ "; } print "$file_tail "; print " ("; print "$browse_revtag:" unless $browse_revtag eq 'HEAD'; print $revision if $revision; print ")"; print qq(
Full Change Log
); print $font_tag; # Print each line of the revision, preceded by its annotation. # $start_of_mark = 0; $end_of_mark = 0; $count = $#revision_map; if ($count == 0) { $count = 1; } $line_num_width = int(log($count)/log(10)) + 1; $revision_width = 3; $author_width = 5; $line = 0; $usedlog{$revision} = 1; foreach $revision (@revision_map) { $text = $text[$line++]; $usedlog{$revision} = 1; if ($opt_html_comments) { # Don't escape HTML in C/C++ comments $text = &leave_html_comments($text); } elsif ( $opt_sanitize ){ # Mark filty words and Escape HTML meta-characters $text = markup_line($text); } else { $text =~ s/&/&/g; $text =~ s//>/g; } # Add a link to traverse to included files $text = &link_includes($text) if $opt_includes; $output = ""; # Highlight lines if (defined($mark_cmd = $mark_line{$line}) && $mark_cmd ne 'end') { $output .= '' if $useAlternateColor; $output .= '' ."
$font_tag"; $inMark = 1; } if ($old_revision ne $revision) { if ($useAlternateColor) { $useAlternateColor = 0; $output .= "
$font_tag" unless $inMark; } else { $useAlternateColor = 1; $output .= '' . ""; #} $output .= "
$font_tag" unless $inMark; } } $output .= sprintf("%${line_num_width}s ", $line) if $opt_line_nums; if ($old_revision ne $revision || $rev_count > 20) { $revision_width = max($revision_width,length($revision)); $output .= "Previous Revision ($prev_revision{$file_rev}) 
"; $output .= '' . "
$font_tag" if $useAlternateColor; $inMark = 0; } print $output; } print "\n"; if ($useLayers) { # Write out cvs log messages as a JS variables # print ""; } &print_bottom; if ( $opt_sanitize ) { dbmclose %SourceChecker::token_dictionary; } ## END of main script sub max { my ($a, $b) = @_; return ($a > $b ? $a : $b); } 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/\(\)//; my ($diff_dir_link) = "cvsview2.cgi?subdir=$rcs_path&files=$file_tail&command=DIRECTORY"; $diff_dir_link .= "&root=$form{'root'}" if defined $form{'root'}; $diff_dir_link .= "&branch=$browse_revtag" unless $browse_revtag eq 'HEAD'; my ($diff_link) = "cvsview2.cgi?diff_mode=context&whitespace_mode=show"; $diff_link .= "&root=$form{'root'}" if defined $form{'root'}; $diff_link .= "&subdir=$rcs_path&command=DIFF_FRAMESET&file=$file_tail"; $diff_link = &url_encode3($diff_link); print "CVS Blame $title_text"; print <<__TOP__ if $useLayers; __TOP__ print '' if not $useLayers; } # print_top sub print_usage { my ($linenum_message) = ''; my ($new_linenum, $src_roots_list); my ($title_text) = "Usage"; if ($ENV{"REQUEST_METHOD"} eq 'POST' && defined($form{'set_line'})) { # Expire the cookie 5 months from now $set_cookie = "Set-Cookie: line_nums=$form{'set_line'}; expires=" .&toGMTString(time + 86400 * 152)."; path=/"; } if (!defined($cookie_jar{'line_nums'}) && !defined($form{'set_line'})) { $new_linenum = 'on'; } elsif ($cookie_jar{'line_nums'} eq 'off' || $form{'set_line'} eq 'off') { $linenum_message = 'Line numbers are currently off.'; $new_linenum = 'on'; } else { $linenum_message = 'Line numbers are currently on.'; $new_linenum = 'off'; } $src_roots_list = join('
', @src_roots); print <<__USAGE__; CVS Blame $title_text

CVS Blame 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
line_nums off * on
off
line numbers
#<line_number> -- #111 jump to a line

Examples:
  cvsblame.cgi?file=ns/cmd/Makefile
  cvsblame.cgi?file=ns/cmd/xfe/mozilla.c&rev=Dogbert4xEscalation_BRANCH
  cvsblame.cgi?file=projects/bonsai/cvsblame.cgi&root=/warp/webroot
  cvsblame.cgi?file=ns/config/config.mk&line_nums=on
  cvsblame.cgi?file=ns/cmd/xfe/dialogs.c#2384

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

* Instead of the line_nums parameter, you can line numbers.
$linenum_message
__USAGE__ &print_bottom; } # sub print_usage sub print_bottom { print <<__BOTTOM__;
Page configuration and help. Mail feedback to <slamm\@netscape.com>. __BOTTOM__ } # print_bottom sub link_includes { my ($text) = $_[0]; if ($text =~ /\#(\s*)include(\s*)"(.*?)"/) { foreach $trial_root (($rcs_path, 'ns/include', "$rcs_path/Attic", "$rcs_path/..")) { if (-r "$root/$trial_root/$3,v") { $text = "$`#$1include$2\"$3\";$'"; last; } } } return $text; } sub html_comments_init { return 0 unless defined($form{'use_html'}) && $form{'use_html'}; # Initialization for C comment context switching $in_comments = 0; $open_delim = '\/\*'; $close_delim = '\*\/'; # Initialize the next expected delim $expected_delim = $open_delim; return 1; } sub leave_html_comments { my ($text) = $_[0]; # Allow HTML in the comments. # $newtext = ""; $oldtext = $text; while ($oldtext =~ /(.*$expected_delim)(.*\n)/) { $a = $1; $b = $2; # pay no attention to C++ comments within C comment context if ($in_comments == 0) { $a =~ s//>/g; $expected_delim = $close_delim; $in_comments = 1; } else { $expected_delim = $open_delim; $in_comments = 0; } $newtext = $newtext . $a; $oldtext = $b; } # Handle thre remainder if ($in_comments == 0){ $oldtext =~ s//>/g; } $text = $newtext . $oldtext; # Now fix the breakage of stuff on xfe. -byrd if ($text =~ /(.*)<(.*@.*)>(.*\n)/) { $text = $1 . "$2" . $3; } return $text; }