#!/usr/bin/perl -w
# -*-perl-*-
##################################################################
# "P4CGI" perl module.
#
#
#
package P4CGI ;
###
# You might need to uncomment and set the lib to point to the perl libraries
#
#use lib '/usr/local/lib/perl5/site_perl' ;
sub ConfigFileName()
{
return "./P4DB.conf" ; # Change here to rename configuration file
}
use CGI ;
use CGI::Carp ;
use strict;
#### Conficuration file name
my $CONFIG_FILE ;
my @ERRLOG ;
my $errors ;
###
### Subroutine that prints the error log
###
sub prerrlog() {
my $res = "" ;
if(@ERRLOG > 0) {
map {
if(/^ERROR:/) { $_ = "" . &fixSpecChar($_) . "" ; }
else { $_ = "" . &fixSpecChar($_) . "" ; }
} @ERRLOG ;
$res .=
"
Log printout:
" .
join("\n",@ERRLOG) .
"
\n" ;
} ;
return $res ;
}
#### The following constants are set or updated by the init() routine
my $VERSION ; # P4DB version
my $P4 ; # Contains p4 command
my $PORT; # Contains p4 port
my $CGI; # Contains CGI object from CGI.pm
my $lastChange ; # Current change level
my $IGNORE_CASE; # "YES" if case should be ignored
my $MAX_CHANGES; # Max changes for change list
my $TAB_SIZE; # Tab size for file viewer
my $USE_JAVA; # Defined if JAVA should be used
my $SHORTCUT_FILE; # Name of file containing shortcuts
my @RESTRICTED_FILES ; # Files where view is restricted
my $HTML_BGCOLOR; # Background color
my $HTML_BACKGROUND ; # Background picture (overrides BGCOLOR if defined)
my $HTML_TEXT_COLOR; # Text color
my $HTML_LINK_COLOR; # Link color
my $HTML_ALINK_COLOR; # Active link color
my $HTML_LINK_TEXT_DEC; # Link/vlink text decoration.
my $HTML_TITLE1_BGCOLOR ;
my $HTML_TITLE1_COLOR ;
my $HTML_HDRFTR_BGCOLOR ;
my $HTML_HDRFTR_COLOR ;
my $ICON_PATH; # Path to icons (usually ../icons or something)
my $HELPFILE_PATH; # Path to help file (html)
my $REDIRECT_ERROR_TO_NULL_DEVICE; # Part of command thar redirects errors to /dev/null
my $REDIRECT_ERROR_TO_STDOUT; # Part of command thar redirects errors to stdout
my $DEBUG ; # When true, prints log
my %PREF ; # Preferences
my %PREF_LIST ; # List of preferences
#### Other package variables
my $pageStartPrinted ; # Used to avoid mutliple headers if an error occurres
my @P4DBadmin ; # Admins for P4DB and Perforce server
my %CONF ; # Hash containing configuration
my %EXTRAHEADER ;
my $helpTarget ; # target for help text
sub init( )
{
#
# Set config file name
#
$CONFIG_FILE = &ConfigFileName ;
## death handler
$SIG{'__DIE__'} = sub { # Thank you Ron Shalhoup for the idea
my($error) = shift;
&P4CGI::bail("Signal caught: $error") ;
exit 0;
};
#
# clear error counter
#
$errors = 0 ;
#
# Set version
#
$VERSION="2.01" ;
# $ VERSION="2.01 (535)" ;
#
# Set configuration defaults
#
$HTML_BGCOLOR = "#f0f0e0" ;
$HTML_BACKGROUND = undef ;
$HTML_TEXT_COLOR = "#000000" ;
$HTML_LINK_COLOR = "#000099" ;
$HTML_ALINK_COLOR = "#000099" ;
$HTML_LINK_TEXT_DEC= "none" ;
$SHORTCUT_FILE = "" ;
$ICON_PATH = "./" ;
$HELPFILE_PATH = "./" ;
$HTML_TITLE1_BGCOLOR = "#ccffff" ;
$HTML_TITLE1_COLOR = "blue" ;
$HTML_HDRFTR_BGCOLOR = "#FFFF99" ;
$HTML_HDRFTR_COLOR = "#e02020" ;
#
# Initiate CGI module
#
$CGI = new CGI ;
$CGI->autoEscape(undef) ;
#
# Setup preference list
#
%PREF_LIST =
(
"JV" => ["x:BOOL",
"Enable experimental java depot tree browser.
(not always available)",0],
"SF" => ["b:LIST","Shortcut file to use",0],
"DP" => ["a:LIST","P4 Depot",0],
"TB" => ["d:INT","Default tab stop",8],
"MX" => ["d:INT","Max changes to show",100],
"IC" => ["d:BOOL","Ignore case (like NT)",0],
"NW" => ["d:BOOL","Open changes in new window",0],
"ST" => ["c:BOOL","Shortcuts on top of main page",0],
"UL" => ["f:BOOL","Underline links",0],
"CL" => ["f:LIST","Color schemes",0],
"VC" => ["f:BOOL","View files with colors",1],
"DBG" => ["z:BOOL","Print log information (for development)",0],
) ;
### Set preferences
%PREF=$CGI->cookie(-name=>"P4DB_PREFERENCES") ; # First try cookie...
my $p ;
foreach $p (keys %PREF_LIST) {
if(! defined $PREF{$p}) {
$PREF{$p} = $ {$PREF_LIST{$p}}[2];
ERRLOG("Set $p!") ;
}
} ;
foreach $p (keys %PREF) {
if(exists $PREF_LIST{$p}) {
ERRLOG("PREF: $p => $PREF{$p} (${$PREF_LIST{$p}}[1])") ;
}
else {
delete $PREF{$p} ;
} ;
} ;
if(defined $CGI->param("SET_PREFERENCES")) {
my $c ;
foreach $c (keys %PREF) {
my $val = $CGI->param($c) ;
if(defined $val) {
my $type = $ {$PREF_LIST{$c}}[0] ;
if($type eq "INT") { $val =~ /^\d+$/ or next ; } ;
if($type eq "BOOL") { $val =~ /^[01]$/ or next ; } ;
$PREF{$c} = $val ;
}
}
}
### Set up data structure for configuration file read
my %configReaderData = ( "P4PATH" => \$P4,
"HTML_ICON_PATH" => \$ICON_PATH,
"HTML_HELPFILE_PATH" => \$HELPFILE_PATH,
"P4DB_ADMIN" => \@P4DBadmin,
"SHELL" => \$ENV{"SHELL"},
"REDIRECT_ERROR_TO_NULL_DEVICE" => \$REDIRECT_ERROR_TO_NULL_DEVICE,
"REDIRECT_ERROR_TO_STDOUT" => \$REDIRECT_ERROR_TO_STDOUT,
"DEPOT" => $PREF_LIST{"DP"},
"SHORTCUT_FILE" => $PREF_LIST{"SF"},
"COLORS" => $PREF_LIST{"CL"},
"\@BGCOLOR" => $PREF_LIST{"BGC"},
"\@BGCOLORT" => $PREF_LIST{"TC1"},
"\@BGCOLORT2" => $PREF_LIST{"TC2"},
"\@TEXTCOLOR" => $PREF_LIST{"TC"},
"\@LINKCOLOR" => $PREF_LIST{"LC"},
) ;
### Read configuration file
local *F ;
my $line = 0 ;
open(F,"<$CONFIG_FILE") or &P4CGI::bail("Could not open config file \"$CONFIG_FILE\" for read") ;
while() {
$line++ ;
chomp ; # Remove newline
s/^\s+// ; # remove leading spaces
next if (/^\#/ or /^\s*$/) ; # Skip if comment or empty line
s/\s+/ /g ; # Normalize all spaces to a single space
s/ $// ; # Remove trailing spaces
# Check syntax and get data
/^(\S+):\s*(.*)/ or
&P4CGI::bail("Parse error in config file \"$CONFIG_FILE\" line $line:\n\"$_\"") ;
# Get values
my ($res,$val) = ($1,$2);
# Make sure config value exist
if(! exists $configReaderData{$res}) {
&P4CGI::bail("Parse error in config file \"$CONFIG_FILE\" line $line:\n\"$_\"") ;
} ;
# Get config value and check type
my $ref = $configReaderData{$res} ;
my $type = ref($ref) ;
$type eq "SCALAR" and do { $$ref = $val ; ERRLOG("$res=$val") ; next ; } ;
$type eq "ARRAY" and do {
if($res =~ /^\@/) {
push @$ref,split /\s+/,$val ;
}
else {
push @$ref,$val ;
} ;
# Potetial security hole, any user can se p4 user and password
# ERRLOG("push $res,$val") ;
next ;
} ;
&P4CGI::bail("Illegal config type $type line $line:\n\"$_\"") ;
}
close F ;
### Set port and p4 command
$PORT = $ {$PREF_LIST{"DP"}}[$PREF{"DP"}+3] ;
if(!defined $PORT) {
$PORT = $ {$PREF_LIST{"DP"}}[3] ;
$PREF{"DP"} = 0 ;
}
bail("DEPOT NOT DEFINED") unless defined $PORT ;
$PORT =~ /(\S+)\s(\S+)\s(\S+)\s(\S+)/ or do { bail("DEPOT line not correct ($PORT)") ; } ;
$PORT = $1 ;
$P4 .= " -p $1 -u $2 -c $3 " ;
if($4 ne "*") {
$P4 .= "-P $4 " ;
} ;
# Potential security hole, any user can se the log..
# push @ERRLOG,"P4 command set to: \"$P4\"" ;
### Set up shortcut file
$SHORTCUT_FILE = $ {$PREF_LIST{"SF"}}[$PREF{"SF"}+3] ;
if(!defined $SHORTCUT_FILE) {
$SHORTCUT_FILE = $ {$PREF_LIST{"SF"}}[3] ;
$PREF{"SF"} = 0 ;
}
$SHORTCUT_FILE =~ s/^(\S+).*/$1/ ;
### Get colors
my $colors = $ {$PREF_LIST{"CL"}}[$PREF{"CL"}+3] ;
if(!defined $colors) {
$colors = $ {$PREF_LIST{"CL"}}[3] ;
$PREF{"CL"} = 0 ;
}
if($colors =~ /^(\S+)\s(\S+)\s(\S+)\s(\S+)\s(\S+)\s(\S+)\s(\S+)\s(\S+)\s(\S+)\s/) {
$HTML_BGCOLOR = $1 ;
$HTML_TEXT_COLOR = $2 ;
$HTML_LINK_COLOR = $3 ;
$HTML_ALINK_COLOR = $4 ;
$HTML_TITLE1_BGCOLOR = $5 ;
$HTML_TITLE1_COLOR = $6 ;
$HTML_HDRFTR_BGCOLOR = $7 ;
$HTML_HDRFTR_COLOR = $8 ;
} ;
### fix undelines
if($PREF{"UL"}) {
$HTML_LINK_TEXT_DEC = "underline" ;
} ;
$IGNORE_CASE = $PREF{"IC"} ? "Yes" : "No" ;
$TAB_SIZE = $PREF{"TB"} ;
$TAB_SIZE = 16 if $TAB_SIZE > 16 ;
$TAB_SIZE = 0 if $TAB_SIZE <= 0 ;
$USE_JAVA = $PREF{"JV"} ? "Yes" : undef ;
$MAX_CHANGES = $PREF{"MX"} ;
foreach (keys %ENV) {
push @ERRLOG,"Environment variable $_: \"". $ENV{$_} . "\"" ;
} ;
#
# Check that we have contact with p4 server
#
$lastChange= undef ;
my $d ;
p4call(\$d,"changes -m 1") ;
$d =~ /Change (\d+)/ and do { $lastChange=$1 ;} ;
} ;
#################################################################
### Documentation start
=head1 NAME
P4CGI - Support for CGI's that interface p4. Written specifically for P4DB
=cut ;
sub CURRENT_CHANGE_LEVEL() { return $lastChange ? $lastChange : -1 ; } ;
sub RESTRICTED() { return @RESTRICTED_FILES ; } ;
sub USER_P4PORT() { return $PORT ; } ;
sub ICON_PATH() { return $ICON_PATH ; } ;
sub HELPFILE_PATH() { return $HELPFILE_PATH ; } ;
sub REDIRECT_ERROR_TO_NULL_DEVICE() { return $REDIRECT_ERROR_TO_NULL_DEVICE ; } ;
sub REDIRECT_ERROR_TO_STDOUT() { return $REDIRECT_ERROR_TO_STDOUT ; } ;
sub SHORTCUT_FILE() { return $SHORTCUT_FILE ; } ;
sub VIEW_WITH_COLORS() { return $PREF{"VC"} ; } ;
sub SHORTCUTS_ON_TOP() { return $PREF{"ST"} ; } ;
sub CHANGES_IN_SEPPARATE_WIN() { return $PREF{"NW"} ; } ;
sub USE_JAVA() { return $USE_JAVA ; } ;
sub MAX_CHANGES() { return $MAX_CHANGES ; } ;
sub HDRFTR_BGCOLOR() { return $HTML_HDRFTR_BGCOLOR ; } ;
sub BGCOLOR() { return $HTML_BGCOLOR ; } ;
sub ERRLOG { push @ERRLOG,@_ ; };
sub ERROR { &ERRLOG(map { "ERROR: $_" } @_) ; $errors++ ; };
sub EXTRAHEADER(% ) { %EXTRAHEADER = @_ ; } ;
sub SET_HELP_TARGET($ ) { $helpTarget = shift @_ ; } ;
sub IGNORE_CASE() { return $IGNORE_CASE ; } ;
sub PREF() { return %PREF ; } ;
sub PREF_LIST() { return %PREF_LIST ; } ;
=head1 SUBROUTINES
=cut ;
###################################################################
### cgi
###
=head2 cgi
C<&P4CGI::cgi()>
Return CGI reference
Example:
my $file = P4CGI::cgi()->param("file") ;
print "File parameter value: $file\n" ;
=cut
;
sub cgi() {
confess "CGI not initialized" unless defined $CGI ;
return $CGI ;
}
###################################################################
### p4call
###
=head2 p4call
C<&P4CGI::p4call(>BC<,>BC<)>
Request data from p4. Calls p4 with command B and returns data in B.
This function is really three different functions depeding in the type of the
B parameter.
=over 4
=item result
This parameter can be of three different types:
=over 4
=item Filehandle (typeglob)
Data from command can be read from filehandle. NOTE! File must be closed by
caller.
=item Reference to array
Returns result from command into array (newlines stripped)
=item Reference to scalar
Returns result from command into scalar. (lines separated by newline)
=back
Any other type of parameter will abort operation
=item command
Command to send to p4 command line client.
=back
Example:
my $d ;
&P4CGI::p4call(\$d,"changes -m 1") ;
$d =~ /Change (\d+)/ or &bail("No contact with P4 server") ;
$lastChange=$1 ;
=cut
;
sub p4call {
my ( $par, @command ) = @_;
my $partype = ref $par ;
push @ERRLOG,"p4call(<$partype>,@command)" ;
if(!$partype) {
open( $par, "$P4 @command|" ) || &bail( "$P4 @command failed" );
return ;
} ;
"ARRAY" eq $partype and do {
local *P4 ;
@$par = () ;
open( P4, "$P4 @command|" ) || &bail( "$P4 @command failed" );
while() {
chomp ;
push @$par,$_ ;
} ;
close P4 ;
return ;
} ;
"SCALAR" eq $partype and do {
$$par = "" ;
local *P4 ;
open( P4, "$P4 @command|" ) || &bail( "$P4 @command failed" );
while() {
$$par .= $_ ;
} ;
close P4 ;
return ;
} ;
die("Called with illegal parameter ref: $partype") ;
} ;
###################################################################
### p4readform
###
=head2 p4readform
C<&P4CGI::p4readform(>B,BC<)>
Reads output from a P4 command and assumes the data is a form (e.g. "client -o").
The form is stored in a hash and the function returns an array containing all
field names in the order they appeared. The hash will contain the field names as
key and field values as data.
=over 4
=item command
Command to send to p4 command line client.
=item resulthash
Reference to a hash to receive reults
=back
Example:
my %fields ;
my @fields = &P4CGI::p4readforml("client -o",\%fields) ;
my $f ;
foreach $f (@fields) {
print "field $f: $fields{$f}\n" ;
}
=cut
;
sub p4readform($\% )
{
my $cmd = shift @_ ;
my $href = shift @_ ;
my @result ;
# clear hash
%$href = () ;
local *F ;
p4call(*F,$cmd) ;
my $cline = ;
while($cline) {
chomp $cline ;
$_ = $cline ;
if(/^\#/ or /^\s*$/) { # skip comments and empty line
$cline = ;
next ;
}
if(/^(\S+):\s*(.*)\s*$/) {
my $fld=$1 ;
my $val=$2 ;
push @result,$fld ;
my $ws ;
if($val eq "") {
$val = undef ;
while(defined ($cline = )) {
$_ = $cline ;
chomp ;
last if /^\w/ ;
s/^\s+// ;
if(defined $val) {
$val .= "\n$_" ;
}
else {
$val = "$_" ;
}
}
}
else {
$cline = ;
}
$$href{$fld}=$val ;
}
else {
$cline = ;
}
}
close *F ;
return @result ;
} ;
###################################################################
### start_page
###
=head2 start_page
C<&P4CGI::start_page(>B[C<,>B