#!/usr/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): use strict; # Shut up misguided -w warnings about "used only once". "use vars" just # doesn't work for me. sub sillyness { my $zz; $zz = $::CI_BRANCH; $zz = $::CI_REPOSITORY; $zz = $::lines_added; $zz = $::lines_removed; $zz = $::query_begin_tag; $zz = $::query_branchtype; $zz = $::query_dirtype; $zz = $::query_date_max; $zz = $::query_debug; $zz = $::query_end_tag; $zz = $::query_filetype; $zz = $::query_logexpr; $zz = $::query_whotype; $zz = $::script_type; } require 'CGI.pl'; $::query_debug = (defined($::FORM{'debug'}) ? 1 : 0); $::query_module = &SanitizeModule($::FORM{'module'}); $::CVS_ROOT = $::FORM{'cvsroot'}; $::CVS_ROOT = &pickDefaultRepository() unless $::CVS_ROOT; &validateRepository($::CVS_ROOT); $::TreeID = $::query_module if (!exists($::FORM{'treeid'}) && defined($::query_module) && exists($::TreeInfo{$::query_module}{'repository'})); $::TreeID = 'default' if (!exists($::TreeInfo{$::TreeID}{'repository'}) || exists($::TreeInfo{$::TreeID}{'nobonsai'})); &LoadTreeConfig(); require 'cvsquery.pl'; my $userdomain = Param('userdomain'); my $registryurl = Param('registryurl'); $registryurl =~ s@/$@@; $| = 1; my $sm_font_tag = ""; my $generateBackoutCVSCommands = 0; if (defined $::FORM{'generateBackoutCVSCommands'}) { $generateBackoutCVSCommands = 1; } my $show_raw = 0; if ($::FORM{'raw'}) { $show_raw = 1; } my $show_xml = 0; my $xml_nofiles = 0; if ($::FORM{'xml'}) { $show_xml = 1; if ($::FORM{'xml_nofiles'}) { $xml_nofiles = 1; } } if( !$show_xml && !$generateBackoutCVSCommands ) { print "Content-type: text/html\n\n"; print setup_script(); } elsif ($show_xml) { print "Content-type: text/xml\n\n"; } elsif ($generateBackoutCVSCommands) { print "Content-type: text/plain\n\n"; } #print "
";


my $CVS_REPOS_SUFIX = $::CVS_ROOT;
$CVS_REPOS_SUFIX =~ s/\//_/g;

my $CHECKIN_DATA_FILE = "data/checkinlog${CVS_REPOS_SUFIX}";
my $CHECKIN_INDEX_FILE = "data/index${CVS_REPOS_SUFIX}";


my $SORT_HEAD="bgcolor=\"#DDDDDD\"";

#
# Log the query
Log("Query [$ENV{'REMOTE_ADDR'}]: $ENV{'QUERY_STRING'}");

#
# build a module map
#

#
# allow ?file=/a/b/c/foo.c to be synonymous with ?dir=/a/b/c&file=foo.c
#
$::FORM{'file'} = "" unless defined $::FORM{'file'};
unless ($::FORM{'dir'}) {
    $::FORM{'file'} = Fix_BonsaiLink($::FORM{'file'});
    if ($::FORM{'file'} =~ m@(.*?/)([^/]*)$@) {
        $::FORM{'dir'} = $1;
        $::FORM{'file'} = $2;
    } else {
        $::FORM{'dir'} = "";
    }
}

#
# build a directory map
#
@::query_dirs = split(/[;, \t\000]+/, $::FORM{'dir'});

$::query_file = $::FORM{'file'};
$::query_filetype = &ExpectMatchtype($::FORM{'filetype'});
$::query_logexpr = $::FORM{'logexpr'};

#
# date
#
$::query_date_type = $::FORM{'date'} || 'nothing';
my $query_hours;
if( $::query_date_type eq 'hours' ){
    $query_hours = &ExpectDigit('hours', $::FORM{'hours'});
    $::query_date_min = time - $query_hours*60*60;
}
elsif( $::query_date_type eq 'day' ){
    $::query_date_min = time - 24*60*60;
}
elsif( $::query_date_type eq 'week' ){
    $::query_date_min = time - 7*24*60*60;
}
elsif( $::query_date_type eq 'month' ){
    $::query_date_min = time - 30*24*60*60;
}
elsif( $::query_date_type eq 'all' ){
    $::query_date_min = 0;
}
elsif( $::query_date_type eq 'explicit' ){
    if ($::FORM{'mindate'}) {
        $::query_date_min = parse_date($::FORM{'mindate'});
    }

    if ($::FORM{'maxdate'}) {
        $::query_date_max = parse_date($::FORM{'maxdate'});
    }
    if (defined($::query_date_min) && defined($::query_date_max) &&
        ($::query_date_max < $::query_date_min)) {
        ($::query_date_min, $::query_date_max) = 
            ($::query_date_max, $::query_date_min);
    }
}
else {
    $::query_date_min = time-60*60*2;
}

#
# who
#
$::query_who = $::FORM{'who'};
$::query_whotype = &ExpectMatchtype($::FORM{'whotype'});
if ($::query_whotype eq 'notregexp' || $::query_whotype eq 'regexp') {
    $::query_who = &SanitizeRegexp($::query_who);
} else {
    $::query_who = &SanitizeUsernames($::query_who);
}

#
# branch
#
$::query_branch = $::FORM{'branch'};
$::query_branchtype = &ExpectMatchtype($::FORM{'branchtype'});
$::query_branch = 'HEAD' if !defined($::query_branch);
if ($::query_branchtype eq 'notregexp' || $::query_branchtype eq 'regexp') {
    $::query_branch = &SanitizeRegexp($::query_branch);
} else {
    $::query_branch = &SanitizeRevision($::query_branch);
}
$::query_dirtype = $::FORM{'dirtype'};
$::query_dirtype = 'recurse' if !defined ($::query_dirtype);

if ($::query_branch eq 'HEAD' && $::query_branchtype ne 'notregexp') {
    $::query_branch_head = 1 ;
}

#
# tags
#
$::query_begin_tag = &SanitizeRevision($::FORM{'begin_tag'});
$::query_end_tag = &SanitizeRevision($::FORM{'end_tag'});


#
# Get the query in english and print it.
#
my ($t, $e);
$t = $e = &query_to_english;
$t =~ s/<[^>]*>//g;

my %mod_map = ();
my $result= &query_checkins( %mod_map );

my %w;

for my $i (@{$result}) {
	my $aname=$i->[$::CI_WHO];
# the else is for compatibility w/ something that uses the other format
# the regexp is probably not the best, but I think it might work
	if ($aname =~ /%\w*.\w\w+/) {
		my $tmp = join("@",split("%",$aname));
		$w{"$tmp"} = 1;
	}else{
		$w{"$i->[$::CI_WHO]\@$userdomain"} = 1;
	}
}

my @p = sort keys %w;
my $pCount = @p;
my $s = join(",%20", @p);

$e =~ s/Checkins in/In/;

my $menu = "

$e

Modify Query "; if ($pCount) { my $persons = "people"; $persons = "person" if ($pCount == 1); $menu .= "
Mail everyone on this page ($pCount $persons)
Show commands which could be used to back out these changes "; } if (defined $::FORM{'generateBackoutCVSCommands'}) { print "Content-type: text/plain # This page can be saved as a shell script and executed. It should be # run at the top of your CVS work area. It will update your workarea to # backout the changes selected by your query. "; unless ($pCount) { print " # # No changes occurred during this interval. # There is nothing to back out. # "; exit; } foreach my $ci (reverse @{$result}) { if ($ci->[$::CI_REV] eq "") { print "echo 'Changes made to $ci->[$::CI_DIR]/$ci->[$::CI_FILE] need to be backed out by hand'\n"; next; } my $prev_revision = PrevRev($ci->[$::CI_REV]); print "cvs update -j$ci->[$::CI_REV] -j$prev_revision $ci->[$::CI_DIR]/$ci->[$::CI_FILE]\n"; } exit; } if( !$show_xml ) { PutsHeader($t, "CVS Checkins", "$menu"); } # # Test code to print the results # $|=1; my $head_who = ''; my $head_file = ''; my $head_directory = ''; my $head_delta = ''; my $head_date = ''; if( !$show_raw && !$show_xml ) { $::FORM{"sortby"} ||= ""; if( $::FORM{"sortby"} eq "Who" ){ $result = [sort { $a->[$::CI_WHO] cmp $b->[$::CI_WHO] || $b->[$::CI_DATE] <=> $a->[$::CI_DATE] } @{$result}] ; $head_who = $SORT_HEAD; } elsif( $::FORM{"sortby"} eq "File" ){ $result = [sort { $a->[$::CI_FILE] cmp $b->[$::CI_FILE] || $b->[$::CI_DATE] <=> $a->[$::CI_DATE] || $a->[$::CI_DIRECTORY] cmp $b->[$::CI_DIRECTORY] } @{$result}] ; $head_file = $SORT_HEAD; } elsif( $::FORM{"sortby"} eq "Directory" ){ $result = [sort { $a->[$::CI_DIRECTORY] cmp $b->[$::CI_DIRECTORY] || $a->[$::CI_FILE] cmp $b->[$::CI_FILE] || $b->[$::CI_DATE] <=> $a->[$::CI_DATE] } @{$result}] ; $head_directory = $SORT_HEAD; } elsif( $::FORM{"sortby"} eq "Change Size" ){ $result = [sort { ($b->[$::CI_LINES_ADDED]- $b->[$::CI_LINES_REMOVED]) <=> ($a->[$::CI_LINES_ADDED]- $a->[$::CI_LINES_REMOVED]) #|| $b->[$::CI_DATE] <=> $a->[$::CI_DATE] } @{$result}] ; $head_delta = $SORT_HEAD; } else{ $result = [sort {$b->[$::CI_DATE] <=> $a->[$::CI_DATE]} @{$result}] ; $head_date = $SORT_HEAD; } &print_result($result); } elsif( $show_xml ) { print "\n"; print "\n"; my $max = @{$result}; my $k = 0; while ($k < $max) { my $ci = $result->[$k]; my @ci_files = ($ci->[$::CI_DIR] . "/" . $ci->[$::CI_FILE]); my @ci_revs = ($ci->[$::CI_REV]); my $ci_log = ""; my $ci_who = $ci->[$::CI_WHO]; my $ci_date = $ci->[$::CI_DATE]; # combine adjacent entries with the same log entry my $j = $k+1; if (($ci_log = $ci->[$::CI_LOG]) ne '') { while ($j < $max && $result->[$j]->[$::CI_LOG] eq $ci_log) { push @ci_files, $result->[$j]->[$::CI_DIR]."/".$result->[$j]->[$::CI_FILE]; push @ci_revs, $result->[$j]->[$::CI_REV]; $j++; } } $ci_who = &html_log($ci_who); $ci_log = &html_log($ci_log); print "\n"; print " $ci_log\n"; if (!$xml_nofiles) { print " \n"; for (my $f = 0; $f <= $#ci_files; $f++) { print " " . &html_log($ci_files[$f]) . "\n"; } print " \n"; } print "\n"; $k = $j; } print "\n\n"; exit; } else { print "

";
    for my $ci (reverse @$result) {
        $ci->[$::CI_LOG] = '';
        $s = join("|",@$ci);
        print "$s\n";
    }
}


#
#
#
sub print_result {
    my ($result) = @_;
    my ($ci,$i,$k,$j,$max, $l, $span);

    &print_head;

    $i = 20;
    $k = 0;
    $max = @{$result};

    while( $k < $max ){
        $ci = $result->[$k];
        $span = 1;
        if( ($l = $ci->[$::CI_LOG]) ne '' ){
            #
            # Calculate the number of consecutive logs that are
            #  the same and nuke them
            #
            $j = $k+1;
            while( $j < $max && $result->[$j]->[$::CI_LOG] eq $l ){
                $result->[$j]->[$::CI_LOG] = '';
                $j++;
            }

            #
            # Make sure we don't break over a description block
            #
            $span = $j-$k;
            if( $span-1 > $i ){
                $i = $j-$k;
            }
        }

        &print_ci( $ci, $span );


        if( $i <= 0 ){
            $i = 20;
            print "\n";
        }
        else {
            $i--;
        }
        $k++;
    }

    &print_foot;
}

my $descwidth;

sub print_ci {
    my ($ci, $span) = @_;

    my ($sec,$minute,$hour,$mday,$mon,$year) = localtime( $ci->[$::CI_DATE] );
    my $t = sprintf("%04d-%02d-%02d %02d:%02d",$year+1900,$mon+1,$mday,$hour,$minute);

    my $log = &html_log($ci->[$::CI_LOG]);
    my $rev = $ci->[$::CI_REV];
    my $url_who = &url_quote($ci->[$::CI_WHO]);
    my $url_dir = &url_quote($ci->[$::CI_DIR]);
    my $url_file = &url_quote($ci->[$::CI_FILE]);

    print "\n";
    print "\n";
}

sub print_head {

if ($::versioninfo) {
    print "";
    print "";
    print "";
    print "";
    print "";
    print "(+$::lines_added/$::lines_removed) Lines changed.";
}

my $anchor = $ENV{QUERY_STRING};
$anchor =~ s/\&sortby\=[A-Za-z\ \+]*//g;
$anchor = "\n";
print "\n";
print "\n";
}


sub print_foot {
    print "
${sm_font_tag}$t"; print "" . "$ci->[$::CI_WHO]\n"; print "[$::CI_REPOSITORY]', '$url_dir'," . "'$url_file','$ci->[$::CI_REV]','$::query_branch',event)\">\n"; # if( (length $ci->[$::CI_FILE]) + (length $ci->[$::CI_DIR]) > 30 ){ # $d = $ci->[$::CI_DIR]; # if( (length $ci->[$::CI_DIR]) > 30 ){ # $d =~ s/([^\n]*\/)(classes\/)/$1classes\/
  /; # # Insert a
before any directory named # # 'classes.' # } # print " $d/
  $ci->[$::CI_FILE]
\n"; # } # else{ # print " $ci->[$::CI_DIR]/$ci->[$::CI_FILE]\n"; # } my $d = "$ci->[$::CI_DIR]/$ci->[$::CI_FILE]"; if (defined $::query_module && $::query_module eq 'allrepositories') { $d = "$ci->[$::CI_REPOSITORY]/$d"; } $d =~ s:/:/:g; # Insert a tag after each slash, so that # we'll break long paths at a reasonable place. print "$d\n"; if( $rev ne '' ){ my $prevrev = &PrevRev( $rev ); print "
${sm_font_tag}$rev\n"; } else { print "\ \n"; } if( !$::query_branch_head ){ print "$ci->[$::CI_BRANCH] \n"; } print "${sm_font_tag}$ci->[$::CI_LINES_ADDED]/$ci->[$::CI_LINES_REMOVED] \n"; if( $log ne '' ){ $log = MarkUpText($log); # Makes numbers into links to bugsplat. $log =~ s/\n/
/g; # Makes newlines into
's if( $span > 1 ){ print "
$log\n"; } else { print "$log\n"; } } print "
$anchor>When\n"; print "${anchor}&sortby=Who>Who\n"; print "${anchor}&sortby=File>File\n"; print "Rev\n"; $descwidth = 47; if( !$::query_branch_head ){ print "Branch\n"; $descwidth -= 2; } print "${anchor}&sortby=Change+Size>+/-\n"; print "Description\n"; print "
"; print "

"; } sub html_log { my ( $log ) = @_; $log =~ s/&/&/g; $log =~ s//>/g; return $log; } sub PrevRev { my( $rev ) = @_; my( $i, $j, $ret, @r ); @r = split( /\./, $rev ); $i = @r-1; $r[$i]--; if( $r[$i] == 0 ){ $i -= 2; } $j = 0; while( $j < $i ){ $ret .= "$r[$j]\."; $j++ } $ret .= $r[$i]; } sub parse_date { my($d) = @_; return &ExpectDate($d); } sub setup_script { my $script_str = qq{ }; return $script_str; } # # Actually do the query # sub query_to_english { my $english = 'Checkins '; $::query_module = 'all' unless defined $::query_module; if( $::query_module eq 'allrepositories' ){ $english .= "to All Repositories "; } elsif( $::query_module ne 'all' && @::query_dirs == 0 ){ $english .= "to module " . html_quote($::query_module) . " "; } elsif( $::FORM{dir} ne "" ) { my $word = "directory"; if (@::query_dirs > 1) { $word = "directories"; } $english .= "to $word " . html_quote($::FORM{dir}) . " "; } if ($::query_file ne "") { if ($english ne 'Checkins ') { $english .= "and "; } if (defined($::query_filetype) && $::query_filetype eq 'regexp') { $english .= "to a file like "; } elsif (defined($::query_filetype) && $::query_filetype eq 'notregexp') { $english .= "to a file not like "; } else { $english .= "to file "; } $english .= "" . &html_quote($::query_file) . " "; } if (!$::query_branch_head) { if ($::query_branch eq '') { $english .= "on all branches "; } else { if ($::query_branchtype eq 'notregexp') { if ($::query_branch eq 'HEAD') { $english .= "not on "; } else { $english .= "not like "; } } elsif ($::query_branchtype eq 'regexp') { $english .= "like "; } else { $english .= "on "; } $english .= "branch " . html_quote($::query_branch) . " "; } } if( $::query_who) { if (defined($::query_whotype) && $::query_whotype eq 'regexp') { $english .= "by someone like "; } elsif (defined($::query_whotype) && $::query_whotype eq 'notregexp') { $english .= "by someone not like "; } else { $english .= "by "; } $english .= "" . &html_quote($::query_who) . " "; } $::query_date_type = $::FORM{'date'} || 'nothing'; if( $::query_date_type eq 'hours' ){ $english .="in the last $query_hours hours"; } elsif( $::query_date_type eq 'day' ){ $english .="in the last day"; } elsif( $::query_date_type eq 'week' ){ $english .="in the last week"; } elsif( $::query_date_type eq 'month' ){ $english .="in the last month"; } elsif( $::query_date_type eq 'all' ){ $english .="since the beginning of time"; } elsif( $::query_date_type eq 'explicit' ){ my ($w1, $w2); if ( $::FORM{mindate} && $::FORM{maxdate}) { $w1 = "between"; $w2 = "and" ; } else { $w1 = "since"; $w2 = "before"; } if( $::FORM{'mindate'}){ my $dd = &parse_date($::FORM{'mindate'}); my ($sec,$minute,$hour,$mday,$mon,$year) = localtime( $dd ); my $t = sprintf("%04d-%02d-%02d %02d:%02d",$year+1900,$mon+1,$mday,$hour,$minute); $english .= "$w1 $t "; } if( $::FORM{'maxdate'}){ my $dd = &parse_date($::FORM{'maxdate'}); my ($sec,$minute,$hour,$mday,$mon,$year) = localtime( $dd ); my $t = sprintf("%04d-%02d-%02d %02d:%02d",$year+1900,$mon+1,$mday,$hour,$minute); $english .= "$w2 $t "; } } return $english . ":"; } PutsTrailer();