eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' & eval 'exec perl -S $0 $argv:q' if 0; # THE PRECEEDING STUFF EXECS perl via $PATH # # $Id: //guest/sandy_currier/utils/p4vtree.pl#2 $ # # # Copyright (c) 2000, Sandy Currier (sandy@releng.com) # Distributed under the GNU GENERAL PUBLIC LICENSE: # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 1, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. # # # Unbuffer STDERR and STDOUT select(STDERR); $| = 1; # Make STDERR be unbuffered. select(STDOUT); $| = 1; # STDOUT too # # set up some globals $ThisCmd = "p4vtree"; # this command name # # local variables $filespecs = ""; $P4 = "p4"; $verbose = 1; $columns = 80; # p4 variables passed in... $filelogswitches = ""; $m = ""; # # now parse any args # the usage message (for -h or on error) $help = "$ThisCmd filespecs Function: $ThisCmd will add label information to a p4 filelog command output. It does it the slow way, so depending on the label database, it may take a while. Args: filespecs A list pf valid filespecs Switches/Options: -h Prints this help message -columns <#> Will limit the output to # columns (def = $columns) -verbose <#> A verbose level (def = $verbose) "; # # parse command line { my($i) = 0; while($i <= $#ARGV) { # scan for a help switch if ($ARGV[$i] =~ /^-h/i) { &DieHelp("", $help); } # scan for switches elsif ($ARGV[$i] =~ /^-n/i) { $printonly = 1; $i++; } elsif ($ARGV[$i] =~ /^-i/i) { $filelogswitches = "$filelogswitches -i"; $i++; } elsif ($ARGV[$i] =~ /^-l/i) { $filelogswitches = "$filelogswitches -l"; $i++; } elsif ($ARGV[$i] =~ /^-debug/i) { $debug = 1; $i++; } # scan for variable definitions (-variable value) elsif ($ARGV[$i] =~ /^-\w+/ and defined($ARGV[$i+1]) and $ARGV[$i+1] !~ /^-[^-]/) { # NOTE: nt has a difficult time with '=' on a command line... # process any variable value switches my($var) = $ARGV[$i]; $var =~ s/^-//; my($value) = $ARGV[$i+1]; if (defined $$var) { $$var = $value; } else { &DieHelp("Unknown parameter '$var'\n", $help); } $i=$i+2; } # catch unsupported switches elsif ($ARGV[$i] =~ /^-/) { &DieHelp("Unsupported switch \"$ARGV[$i]\"\n", $help); } else { push @filespecs, $ARGV[$i]; $i++; } } } # # fold in $m if defined if ($m) { $filelogswitches = "$filelogswitches -m $m"; } # # if debugging, re-arrange variables if ($debug) { $verbose = 2; } # # get a list of labels { my @tmp; @tmp = &ExecuteP4Cmd("$P4 labels"); # if an error is returned, just keep going... chomp(@tmp); foreach my $label (@tmp) { my($content) = $label; $label =~ s|^Label (.+) \d\d\d\d/\d\d/\d\d \'.*$|$1|o; # push @Labels, $label; $Labels{$label} = $content; } } # # for each label, create a local hash of the files { my($printhack); foreach my $label (keys(%Labels)) { foreach my $fs (@filespecs) { my(@output); &PrintRaw(".") if ($verbose); $printhack++; if ($filelogswitches =~ /-i/) { # if -i is specified, have no idea what may have been integrated into... @output = &ExecuteP4Cmd("$P4 files //...\@$label"); } else { # if no -i, just use this name @output = &ExecuteP4Cmd("$P4 files $fs\@$label"); } next if (grep(/ - file\(s\) not in label\.$/, @output)); chomp(@{$Files{$label}} = @output); } } &PrintRaw("\n") if ($printhack and $verbose); } # # run the p4 filelog command and collect output (let filelog expand wildcards) { my(@filelog, $currentFile, $currentRev, $printhack); my($filespecs); foreach my $fs (@filespecs) { $filespecs = "\"$fs\""; } @filelog= &ExecuteP4Cmd("$P4 filelog $filelogswitches $filespecs"); # if an error - just keep on going chomp(@filelog); # print it foreach my $line (@filelog) { my($foo); if ($line =~ /^\/\//) { $currentFile = $line; $foo = $line; $foo = substr($foo, 0, $columns) if ($columns > 8); &PrintRaw("$foo\n"); } elsif ($line =~ /^\.\.\. \#(\d+) /) { $currentRev = $1; $foo = $line; $foo = substr($foo, 0, $columns) if ($columns > 8); &PrintRaw("$foo\n"); # add label stuff here foreach my $label (sort(keys(%Labels))) { my($safestring) = quotemeta("$currentFile#$currentRev"); if (grep(/^$safestring - /, @{$Files{$label}})) { my($foo) = $Labels{$label}; $foo = substr($foo, 0, $columns - 8) if ($columns > 8); &PrintRaw("... ... $foo\n"); } } } else { my($foo) = $line; $foo = substr($foo, 0, $columns) if ($columns > 8); &PrintRaw("$foo\n"); } } } # # the end &TheEnd(); # # Subroutines # sub DieHelp { my($str, $help) = @_; print STDERR "$err $str\nUsage: $help"; $Error{'Errors'}++; &TheEnd(); } # should not be called by a server sub TheEnd { my($tmp); # exit with the number of errors in the bottom 16 bits # and the number of warnings in the top # Note: make sure that if things shift off, that error is at least still set $tmp |= $Error{'Errors'}; # explicitly set $! to the explicit value # see the documentation on die exit($tmp); } # # Note: this will actually execute any command... # returns the action of the revision of the specified file#revision sub ExecuteP4Cmd { my($script, $verbose, $print_output, $no_error_check, $stream_p) = @_; my(@output); if ($stream_p) { print $stream_p "$vb\n$vb running: $script\n$vb\n" if ($verbose); } else { print STDOUT "$vb\n$vb running: $script\n$vb\n" if ($verbose); } if (!$Platform{'nt'} and $Platform{'os'} eq "win32") { @output = `$script` unless ($printonly); } else { @output = `$script 2>&1` unless ($printonly); } if ($stream_p) { print $stream_p "@output" if ($print_output); } else { print STDOUT "@output" if ($print_output); } if (!$no_error_check and $?) { # now what - just keep going &PrintError("$ThisCmd - something happened with '$script'\n$?", $stream_p); } return(@output); } sub PrintRaw { my($text, $stream_p) = @_; my($tmp); # print and log (maybe) if ($stream_p) { print $stream_p "$text"; } else { print STDOUT "$text"; } $LogOutput = "$LogOutput$text" if (defined($LogOutput)); return($tmp); } sub PrintError { my($text, $stream_p) = @_; my($tmp); # first, increment error count $Error{'Errors'}++; # make sure $? is set $? = 1; # prepend with the correct prefix $text =~ s/^(.*)$/$err $1/gm; # add a \n $text = "$text\n"; # print and log (maybe) if ($stream_p) { print $stream_p "$text"; } else { print STDERR "$text"; } $LogOutput = "$LogOutput$text" if (defined($LogOutput)); return($tmp); } # can handle, somewhat, either # or @... # Note: the output of a 'p4 change ...' will not be of the form # ... //depot/main/scm/tests/bar#4 edit # ... //depot/main/scm/tests/xxx#1 add # ... //depot/main/scm/tests/zzz#1 add # # the output of s 'p4 files ...' will be something like # //depot/main/scm/tests/foo#4 - edit change 1833 (text) # try to handle both here... sub SplitFilename3 { my($thing) = @_; my($f, $tmp, $r, $a, $d, $junk); if ($thing =~ /\#/){ ($f, $tmp) = split('#', $thing); $d = "\#"; } elsif ($thing =~ /\@/) { ($f, $tmp) = split('@', $thing); $d = "\@"; } else { # hoping that the thing passed in is really a file... $f = $thing; } return($f, $r, $a, $d) unless ($tmp); # if empty $tmp, just return now if ($tmp =~ / - /) { ($r, $a) = split(/ - /, $tmp); # split on the first ' - ' (here's hoping again) } else { # if no ' - ', split on first space... ($r, $a) = split(/ /, $tmp); } ($a, $junk) = split(' ', $a); # just use first word return($f, $r, $a, $d); }