- #!/usr/bin/perl -w
- # -*- perl -*-
- use P4CGI ;
- use strict ;
- use CGI::Carp ;
- #
- #################################################################
- # CONFIGURATION INFORMATION
- # All config info should be in P4CGI.pm
- #
- #################################################################
- #
- # P4 file log viewer
- #
- #################################################################
-
- sub offsetOf($@ ) {
- my $v = shift @_ ;
- my $pos = 0 ;
- while(@_ > 0) {
- if($v eq (shift @_)) {
- return $pos ;
- }
- $pos++ ;
- }
- return -1 ;
- }
-
- my $err2null = &P4CGI::REDIRECT_ERROR_TO_NULL_DEVICE() ;
- my $err2stdout = &P4CGI::REDIRECT_ERROR_TO_STDOUT() ;
-
- local *P ;
-
- # File argument
- my $file = P4CGI::cgi()->param("FSPC") ;
- &P4CGI::bail("No file spec") unless defined $file ;
-
- # Label x-reference argument
- my $listLabel = P4CGI::cgi()->param("LISTLAB") ;
- $listLabel = "No" unless defined $listLabel ;
-
- # Show branch info argument
- my $showBranch = P4CGI::cgi()->param("SHOWBRANCH") ;
- $showBranch="No" unless defined $showBranch ;
-
- # Get file data
- my @filelog ;
- &P4CGI::p4call(\@filelog,"filelog \"$file\"") ;
-
- &P4CGI::bail("No data for file \"$file\"") if @filelog == 0 ;
-
- # Get info about opened status
- &P4CGI::p4call(*P,"opened -a \"$file\" $err2null") ;
- my %opened ;
- my $openedText = "" ;
- while(<P>) {
- $openedText = "Opened by" ;
- chomp ;
- /\w+\#(\d+) - .* by (\w+)\@(\S+)/ or
- &P4CGI::bail("Can not read info from \"p4 opened\"") ;
- my $user = &P4CGI::ahref(-url => "userView.cgi",
- "USER=$2",
- "$2") ;
-
- my $client = &P4CGI::ahref(-url => "clientView.cgi",
- "CLIENT=$3",
- "$3") ;
-
- if(exists $opened{$1}) {
- $opened{$1} .= "<br> and $user\@$client" ;
- } else {
- $opened{$1} = "$user\@$client" ;
- } ;
- } ;
- close *P ;
-
-
- # Get list of labels (if $listLabel is set)
- my @labels ;
- if($listLabel eq "Yes") {
- &P4CGI::p4call(*P,"labels") ;
- while(<P>) {
- /^Label (\S+)/ and do { push @labels,$1 ; } ;
- }
- close P ;
- }
- # Create hash containing labels by file name and
- # version
- my %fileToLabels ;
- if(@labels > 0) {
- # Try to speed things up by looking up
- # file view for each label and removing all
- # labels that don't match
- # This is an act of desperation because in our
- # p4 depot the label search takes forever (well..
- # a long time, 20 secs or so...)
- if(1) {
- my $l ;
- my @l ;
- LABEL: foreach $l (@labels) {
- my %data ;
- &P4CGI::p4readform("label -o \"$l\"",\%data) ;
- if(exists $data{"View"}) {
- my @v = split("\n",$data{"View"}) ;
- foreach (@v) {
- # p4-to-perl regexp conversion
- my $in = $_ ;
- my $re = "" ;
- while($in =~ s/(.*?)(\Q...\E|\Q*\E)//) {
- $re .= "\Q$1\E" ;
- if($2 eq "...") { $re .= ".*" ; }
- else { $re .= "[^/]*" ; }
- }
- $re .= "\Q$in\E" ;
- if($file =~ /$re/) {
- push @l,$l ;
- next LABEL ;
- }
- }
- }
- }
- my $lb = @labels ;
- my $la = @l ;
- &P4CGI::ERRLOG("reduced from $lb to $la labels") ; # DEBUG
- @labels = @l ;
- }
- # <RANT>
- # Frankly, I find it very strange that I can speed
- # up the search by "manually" reading all label
- # specs, parsing them, and checking if the file
- # matches any part of the view before actually
- # asking p4 to do it. Some developer must have had
- # a bad day at perforce. And p4 is not open
- # source.... sigh.
- # </RANT>
-
-
- my $filelabels = "" ;
- foreach (@labels) {
- $filelabels .= " \"$file\@$_\"" ;
- }
- my @filesInLabels ;
- &P4CGI::p4call(\@filesInLabels,"files $filelabels $err2stdout") ;
- my $l ;
- # Remove labels not in list
- # NOTE! The errors (file not in label-messages)
- # are printed to stderr and there
- # is no guarantee that output from stderr and
- # stdout will come in order. This is why
- # we first must figure out which labels
- # that NOT affected the file
- foreach $l (reverse map {/.*@(\S+)\s.*not in label/?$1:()} @filesInLabels) {
- my $offset = offsetOf($l,@labels) ;
- splice @labels,$offset,1 ;
- }
- # Build file-to-label hash. Use only data from
- # stdout (not stderr). (grep is used to filter)
- foreach (grep(!/not in label/,@filesInLabels)) {
- my $lab = shift @labels ;
- /^(\S+)/ ;
- if(defined $fileToLabels{$1}) {
- $fileToLabels{$1} .= "<br>$lab" ;
- }
- else {
- $fileToLabels{$1} = "$lab" ;
- }
- }
- } ;
-
-
- my @legendList ;
- push @legendList,
- "<b>Revision Number</b> -- see the file text",
- "<b>Action</b> -- see the deltas (diffs)",
- "<b>User</b> -- see info about user",
- "<b>Change</b> -- see the complete change description, including other files",
- &P4CGI::ahref("-url","changeList.cgi",
- "FSPC=$file",
- "Changes") . "-- see list of all changes for this file" ;
-
- my @parsListLab ;
- my @parsShowBranch ;
- my $p ;
- foreach $p (&P4CGI::cgi()->param()) {
- push @parsListLab, "$p=" . &P4CGI::cgi()->param($p) unless $p eq "LISTLAB" ;
- push @parsShowBranch, "$p=" . &P4CGI::cgi()->param($p) unless $p eq "SHOWBRANCH" ;
- }
-
- if($listLabel ne "Yes") {
- push @legendList,
- &P4CGI::ahref(@parsListLab,
- "LISTLAB=Yes",
- "List labels") . "-- list cross ref. for labels" ;
- } ;
- if($showBranch ne "No") {
- push @legendList,
- &P4CGI::ahref(@parsShowBranch,
- "SHOWBRANCH=No",
- "Hide branch info") . "-- hide info about branches, merges and copy of file" ;
- }
- else {
- push @legendList,
- &P4CGI::ahref(@parsShowBranch,
- "SHOWBRANCH=Yes",
- "Show branch info") . "-- show info about branches, merges and copy of file" ;
- } ;
-
- # Get file directory part
- my $fileDir=$file ;
- $fileDir =~ s#/[^/]+$## ;
- push @legendList,
- &P4CGI::ahref("-url","depotTreeBrowser.cgi",
- "FSPC=$fileDir",
- "Browse directory") .
- "-- Browse depot tree at $fileDir" ;
-
-
- print "",&P4CGI::start_page("File log<br>$file",&P4CGI::ul_list(@legendList)) ;
-
- my $labelHead ="";
- if($listLabel eq "Yes") {
- $labelHead="In label(s)" ;
- } ;
-
- print
- "",
- &P4CGI::start_table(""),
- &P4CGI::table_header("Rev/view file",
- "Action/view diff",
- "Date",
- "User/view user",
- "Change/view change",
- "Type",
- "Desc",
- $labelHead,
- $openedText) ;
-
- my $log ;
- my @revs ;
- my %relatedFiles ;
- my ($rev,$change,$act,$date,$user,$client,$type,$desc) ;
- my $chbuffer = "" ;
- while($log = shift @filelog) {
- $_ = &P4CGI::fixSpecChar($log) ;
- if(/^\.\.\. \#(\d+) \S+ (\d+) (\S+) on (\S+) by (\S*)@(\S*) (\S*)\s*'(.*)'/ ) {
- print $chbuffer ;
- $chbuffer = "" ;
- ($rev,$change,$act,$date,
- $user,$client,$type,$desc) = ($1,$2,$3,$4,$5,$6,$7,$8) ;
- $type =~ s/\((.*)\)/$1/ ;
- $desc = &P4CGI::magic($desc) ;
- push @revs,$rev ;
- my $labels = $fileToLabels{"$file\#$rev"} ;
- $labels = "" unless defined $labels ;
- $labels = "<b>$labels</b>" ;
- if ($act eq 'branch') {
- $chbuffer .=
- &P4CGI::table_row(-valign => "top",
- &P4CGI::ahref("-url","fileViewer.cgi",
- "FSPC=$file",
- "REV=$rev",
- "$rev"),
- "$act",
- "$date",
- &P4CGI::ahref(-url => "userView.cgi" ,
- "USER=$user",
- "$user"),
- &P4CGI::ahref("-url","changeView.cgi",
- "CH=$change",
- "$change"),
- "$type",
- "<tt>$desc</tt>",
- $labels,
- exists $opened{$rev}?$opened{$rev}:"") ;
- }
- elsif ($act eq 'delete') {
- $chbuffer .=
- &P4CGI::table_row(-valign => "top",
- "$rev",
- "<strike>delete</strike>",
- "$date",
- &P4CGI::ahref(-url => "userView.cgi" ,
- "USER=$user",
- "$user"),
- &P4CGI::ahref("-url","changeView.cgi",
- "CH=$change",
- "$change"),
- "$type",
- "<tt>$desc</tt>",
- $labels,
- exists $opened{$rev}?$opened{$rev}:"") ;
- }
- else {
- $chbuffer .=
- &P4CGI::table_row(-valign => "top",
- &P4CGI::ahref("-url","fileViewer.cgi",
- "FSPC=$file",
- "REV=$rev",
- "$rev"),
- &P4CGI::ahref("-url","fileDiffView.cgi",
- "FSPC=$file",
- "REV=$rev",
- "ACT=$act",
- "$act"),
- "$date",
- &P4CGI::ahref(-url => "userView.cgi" ,
- "USER=$user",
- "$user"),
- &P4CGI::ahref("-url","changeView.cgi",
- "CH=$change",
- "$change"),
- "$type",
- "<tt>$desc</tt>",
- $labels,
- exists $opened{$rev}?$opened{$rev}:"") ;
- }
- }
- else {
- if(/^\.\.\. \.\.\. (\w+) (\w+) (\S+?)\#(\S+)/) {
- my ($op,$direction,$ofile,$orev) = ($1,$2,$3,$4) ;
- my $file = $ofile ;
- $file =~ s/\#.*$// ;
- $relatedFiles{$file} = 1 ;
- if($showBranch ne "No") {
- my ($b1,$b2) = ("","") ;
- if($op eq "copy") {
- ($b1,$b2) = ("<b> ! ","</b>") ;
- }
- my $d = &P4CGI::table_row(-valign => "top",
- "",
- undef,
- undef,
- undef,
- undef,
- undef,
- undef,
- undef,
- undef,
- "$b1$op $direction ".
- &P4CGI::ahref("-url","fileLogView.cgi",
- "FSPC=$ofile",
- "$ofile\#$orev"). "$b2") ;
-
- if($direction ne "from") {
- $chbuffer = "$d\n$chbuffer" ;
- }
- else {
- print "$chbuffer\n$d\n" ;
- $chbuffer = "" ;
- }
- }
- }
- }
- }
- print "$chbuffer\n" ;
-
- print
- "",
- &P4CGI::end_table("") ;
-
- if(@revs > 2) {
- print
- "<hr>",
- &P4CGI::cgi()->startform("-action","fileDiffView.cgi",
- "-method","GET"),
- &P4CGI::cgi()->hidden("-name","FSPC",
- "-value",&P4CGI::fixspaces("$file")),
- &P4CGI::cgi()->hidden("-name","ACT",
- "-value","edit"),
- "\nShow diff between revision: ",
- &P4CGI::cgi()->popup_menu(-name =>"REV",
- "-values" =>\@revs);
- shift @revs ;
- print
- " and ",
- &P4CGI::cgi()->popup_menu(-name =>"REV2",
- "-values" =>\@revs),
- " ",
- &P4CGI::cgi()->submit(-name =>"Go",
- -value =>"Go"),
- &P4CGI::cgi()->endform() ;
- } ;
-
- sub getRelatedFiles($ )
- {
- my $file = shift @_ ;
- my @data ;
- &P4CGI::p4call(\@data,"filelog \"$file\"") ;
- my %res ;
- map { if(/^\.\.\. \.\.\. \w+ \w+ (\S+?)\#/) { $res{$1} = 1 ; } ; } @data ;
- return ( sort keys %res ) ;
- } ;
-
-
- if((keys %relatedFiles) > 0) {
- my @rel = sort keys %relatedFiles ;
- my @fileLinks = map { &P4CGI::ahref("-url","fileLogView.cgi",
- "FSPC=$_",
- "$_") ; } @rel ;
- my %indrel ;
- $relatedFiles{$file} = 1 ;
- while(@rel > 0) {
- my $r ;
- foreach $r (map { exists $relatedFiles{$_} ? () : $_ } getRelatedFiles(shift @rel)) {
- &P4CGI::ERRLOG("found: $r") ;
- $indrel{$r} = 1;
- push @rel, $r ;
- $relatedFiles{$r} = 1 ;
- }
- }
-
- my @indFileLinks = map { &P4CGI::ahref("-url","fileLogView.cgi",
- "FSPC=$_",
- "$_") ; } sort keys %indrel ;
-
- print
- "",
- &P4CGI::start_table(),
- &P4CGI::table_row({ -valign => "top",
- -align => "right",
- -type => "th",
- -text => "Related files:" },
- { -text => &P4CGI::ul_list(@fileLinks) }) ;
- if(@indFileLinks > 0) {
- print "", &P4CGI::table_row({ -valign => "top",
- -align => "right",
- -type => "th",
- -text => "Indirect:" },
- { -text => &P4CGI::ul_list(@indFileLinks) }) ;
- } ;
- print "", &P4CGI::end_table() ;
- } ;
-
-
- print
- "",
- &P4CGI::end_page() ;
-
- #
- # That's all folks
- #