#! /usr/bin/env perl
# FormInfo.pm
########################################################################
########################################################################
# FORM INFO SUBROUTINE
#
# Programmed by David Weintraub
# Date: 09-Nov-2006
# Purpose: To Get Field Values from a Perforce form
#
# Subroutines Included:
# formRead
# ztagRead
# formWrite
# textFormRead
# formDelete
#
# For more information, generate the POD documentation using the
# programs "pod2text", "pod2html", or "pod2man" which are included
# in all Perl installations. See example below:
#
# $ pod2text FormInfo.pm > FormInfo.pm
# $ pod2html FormInfo.pm > FormInfo.html
#
########################################################################
########################################################################
# POD DOCUMENTATION
#
=pod
=head1 NAME
FormInfo.pm - Perl Module for Reading and Writing Perforce data
=head1 SYNOPSIS
Perforce uses I<forms> for inputing and outputing information for its
commands. These functions help the user to use Perforce I<forms> by
wrapping the forms inside Perl hashes.
Sometimes, a hash value will be multiple lines long. In some functions, a
multiple lined entry will always be a multiple line scalar text value. In
I<ztag> functions, the value inside a particular hash entry might be a
reference to an array with each entry in the array containing a single
value. You can use the Perl I<ref> function to test for these cases.
=head1 Functions
=cut
#
########################################################################
########################################################################
# PERL PRAGMAS
#
package FormInfo;
use strict;
use warnings;
#
########################################################################
########################################################################
# PERL MODULES
#
use Carp;
########################################################################
# MODULE EXPORT INFORMATION
#
BEGIN {
use Exporter ();
our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
$VERSION = 1.00;
@ISA = qw(Exporter);
@EXPORT = qw(formRead ztagRead formWrite textFormRead formDelete);
%EXPORT_TAGS = ();
}
#
########################################################################
########################################################################
# PACKAGE VARIABLES
#
our $debug = 0; #For debugging purposes. Can be set locally
our $error = 0; #Error Code
our $errorMsg= ""; #Text Error Message
our $die = 1; #Allow Dying on errors
#
########################################################################
########################################################################
# DEBUGGING
#
sub debug {
if ($debug != 0) {
print qq( ) x $_[1] . qq(DEBUG: $_[0]\n);
}
}
#
########################################################################
########################################################################
# CHECK THAT YOU'RE LOGGED IN
#
sub loggedIn {
my $message = qx(p4 login -s);
if ($?) {
if ($die) {
croak qq(Not logged into Perforce!\n);
} else {
$error = 4;
$errorMsg = qq(Not logged into Perforce!);
return 0;
}
}
return 1;
}
#
########################################################################
########################################################################
# SUBROUTINE FORMREAD
#
=pod
=head2 Subroutine formRead
Purpose: To read information from a Perforce form into a Perl Hash.
This function simply parses the output of a Perforce form and determines
the field value by parsing the plain output of the form. Each key in the
hash will be a field in the form, and each value will be a simple
scalar. Multiple line values will be multiple line scalar text values.
=head3 Call:
C<formRead("$p4Cmd", "$id", \%hash)>
=head4 Parameters Used:
=over 4
=item B<$p4Cmd:>
Perforce Form Type Command (job, client, change, etc)
=item $id
Id of that form type (job #, change #, user Id, etc)
=item %hash
The Perl hash that will contain the information from the form.
B<Notice that this is called as a reference!>. Remember to put the
backslash in front of the hash.
Each field on the form will be a key in the hash, and all multiple line
fields will be a single multiple line value. There is no whitespace in
front of any of the lines.
the form.
=back
=head3 Return Values
=over 4
=item On Success
Number of members in the hash
=item On Failure
Zero
=back
=cut
#
########################################################################
########################################################################
#
#
sub formRead {
my ($p4Cmd, $cmdId, $hashRef) = @_;
unless (loggedIn()) {
return 0; #$FormInfo::error already set
}
#
# ####Test Command To See If Valid
#
open(P4CMD, qq(p4 -s $p4Cmd -o $cmdId |));
chomp (my $response = <P4CMD>);
close (P4CMD);
if ($response =~ /^error:/) { #Perforce Error Detected
if ($die) {
croak qq(Can't execute "p4 $p4Cmd -o $cmdId"\n);
} else {
$error = 1; #Can't execute command
$errorMsg = qq(Can't execute "p4 $p4Cmd -o $cmdId"\n);
return 0;
}
}
#
# ####Command Valid: Go Execute Program
#
open(P4CMD, qq(p4 $p4Cmd -o $cmdId |));
my $currentField;
while (<P4CMD>) {
chomp;
next if /^#/; #Skip Comment Lines
next if /^$/; #Skip Blank Lines
if (/^(\S+):\t(.+)$/) { #Like: Change: 1234
my $field = $1;
my $value = $2;
$hashRef->{"$field"} = $value;
debug(qq(READING: \$hashRef->{"$field"} = $value), 2);
} elsif (/^(\S+):$/) { #Like: Description:
my $field = $1;
$currentField = $field;
} elsif (/^\t(.*)/) { #Like: This is the Description line
my $value = $1;
if (defined($hashRef->{"$currentField"})) {
$hashRef->{"$currentField"} .= "\n" . $value;
} else {
debug(qq(READING: \$hashRef->{"$currentField"} = $value), 2);
$hashRef->{"$currentField"} = $value;
}
}
}
my $returnValue = scalar keys(%{$hashRef});
close (P4CMD);
if ($returnValue) {
return scalar keys(%{$hashRef});
} else {
$error = 3;
$errorMsg = "Command didn't execute!";
return 0;
}
}
#
########################################################################
########################################################################
# SUBROUTINE ZTAGREAD
#
=pod
=head2 Subroutine ztagRead
To read information from a Perforce form into a Perl Hash. This
function uses the "ztag" option to determine the fields and their
values.
Multiple line fields in a form are sometimes returned as a single
multiple line scalar text value. Othertimes, it is returned as an
array. That means the hash is a B<reference> to an array. Therefore,
you need to test the return value of the field with a Perl I<ref>
function to determine whether this field is a scalar value or an
array. For example:
ztagRead("client", "default", \%client)
foreach my $key (keys(%client)) {
if (ref($client{"$key"} eq "ARRAY") {
@myArray = @{$client{"key"}};
for (my $item; $item <= $#myArray; $item++) {
print qq(\$client{"$key"}->[$item] = "@myArray[$item]");
}
} else {
print qq(\$client{$key} = "$client{$key}"\n);
}
=head3 Call:
C<ztagRead("$p4Cmd, "$id", \%hash);>
Note that I<ztagRead> can use the following Perforce commands that
I<formRead> cannot take:
=over 4
=item *
p4 info
=item *
p4 protect
=item *
p4 fstat
=item *
p4 info
=back
In cases where the $id doesn't make sense (like in C<p4 info>), you can
leave this field as a null string, but not blank.
=head4 Parameters Used:
=over 4
=item $p4Cmd
Perforce Form Type Command (job, client, change, etc)
=item id
Id of that form type (job #, change #, user Id, etc)
=item %hash
The Perl hash that will contain the information from the form.
B<Notice that this is called as a reference!>. Remember to put the
backslash in front of the hash.
Each field on the form will be a key in the hash, some of the fields
will be scalars, and some might be references to an array. Becareful!
=back
=head4 Return Values
=over 4
=item On Success
Number of members in the hash
=item On Failure
0 (Zero)
=back
=cut
#
########################################################################
########################################################################
#
#
sub ztagRead {
my ($p4Cmd, $cmdId, $hashRef) = @_;
unless (loggedIn()) {
return 0; #$FormInfo::error already set
}
my $cmd;
if (($p4Cmd eq "describe") or ($p4Cmd eq "fstat")) {
$cmd = qq(-ztag $p4Cmd $cmdId);
} elsif ($p4Cmd eq "info") {
$cmd = qq(-ztag $p4Cmd);
} else {
$cmd = qq(-ztag $p4Cmd -o $cmdId);
}
#
# ####Test if Command is Valid
#
open (P4CMD, qq(p4 -s $cmd|));
chomp (my $response = <P4CMD>);
if ($response =~ /^error:/) {
if ($die) {
croak qq(Can't execute "p4 $cmd"\n);
} else {
$error = 1;
$errorMsg = qq(Can't execute "p4 $cmd"\n);
return 0;
}
}
open(P4CMD, qq(p4 $cmd|)); #Command Valid: Execute Real Command
my $currentField;
while (<P4CMD>) {
chomp;
if (/^\.\.\. (\S+) (.*)$/) { #Like: ... Change 1234
my $field = $1;
my $value = $2;
$currentField = $field;
if ($field =~ /^(.*\D)(\d+)$/) { #Field is an array
$field = $1;
my $index = $2;
$hashRef->{"$field"}->[$index] = $value;
} else {
$hashRef->{"$field"} = $value;
}
} elsif (/^(.+)$/) { #Continuation of Previous Field
$hashRef->{"$currentField"} .= "\n" . $1;
} else { #End of Field
$currentField = "";
}
}
close (P4CMD);
my $returnValue = scalar keys(%{$hashRef});
if ($returnValue) {
return scalar keys(%{$hashRef});
} else {
$error = 3;
$errorMsg = "Command didn't execute!";
return 0;
}
}
#
########################################################################
########################################################################
# SUBROUTINE FORMWRITE
#
=pod
=head2 Subroutine formWrite
Purpose: To write information from a Perl Hash into a Perforce form
=head3 Call:
C<formWrite("$p4Cmd", \%hash);>
=head4 Parameters Used:
=over 4
=item $p4Cmd
Perforce form type command (job client, change, etc)
=item %hash
Hash containing the fields and their values to write to the Perforce
form. Notice that this is called as a reference to a Hash, so don't
forget the backslash in front!
Also take note that all fields must be scalar values! No references to
an Array. If you use ztagRead, you must join the arrays into a single
multiple line scalar field before calling this function.
Also note that you do not need whitespace on each and every line of a
multiple line scalar field (like you would in an actual Perforce
form).
=back
=head4 Return Values
=over 4
=item On Success
Number of lines in hash if successful.
=item On Failure
0 (Zero)
=back
=cut
#
########################################################################
########################################################################
#
#
sub formWrite {
my ($p4Cmd, $hashRef) = @_;
debug(qq(WRITING: open(P4CMD, qq(|p4 $p4Cmd -i))),1);
unless (loggedIn()) {
return 0; #$FormInfo::error already set
}
unless (open(P4CMD, qq(|p4 $p4Cmd -i))) {
if ($die) {
croak qq(Can't execute "p4 $p4Cmd -i"\n);
} else {
$error = 1;
$errorMsg = qq(Can't execute "p4 $p4Cmd -i"\n);
}
}
foreach my $key (keys(%{$hashRef})) {
(my $fieldValue = $hashRef->{"$key"}) =~ s/^/\t/mg;
debug(qq(WRITING: "$key:" . $fieldValue, 2));
print P4CMD "$key:" . $fieldValue . "\n";
}
unless (close (P4CMD)) {
if ($die) {
croak qq(Error in closing "p4 $p4Cmd -i": $?\n);
} else {
$error = $?;
$errorMsg = qq(Error in closing "p4 $p4Cmd -i": $?\n);
return 0;
}
} else {
return scalar keys(%{$hashRef});
}
}
#
########################################################################
########################################################################
# SUBROUTINE TEXT FILE READ
#
=pod
=head2 Subroutine textFormRead
Purpose: To read informtion from a text file in the format of a Perforce
form. This is useful for triggers where the information needed for the
trigger is stored in a text file.
=head3 Call:
C<textFormRead ("$textFile", \%hash);>
=head4 Parameters Used:
=over 4
=item $textFile
The name of the text file that Perforce created on a form out
=item %hash
The hash that will contain the fields and their values. Notice that
all fields are scalar fields as if this was a normal I<formRead> and
not a I<ztagRead>.
Also notice that the %hash is called as a reference, so don't forget
to put a backslash in front!
=back
=head4 Returns:
=over 4
=item On Success
Number of lines in hash if successful.
=item On failure
0 (Zero)
=back
=cut
########################################################################
#
#
sub textFormRead {
my ($textFile, $hashRef) = @_;
unless (loggedIn()) {
return 0; #$FormInfo::error already set
}
unless (open(TEXTFILE, "$textFile")) {
if ($die) {
croak qq(Cannot open file "$textFile" for reading\n);
} else {
$error = 1;
$errorMsg = qq(Can't open "$textFile" for reading\n);
return 0;
}
}
my $previousField;
my $numOfLines = 0;
my $fieldCount = 0;
while (<TEXTFILE>) {
$numOfLines++;
chomp;
if (/^#/) { #Ignore comment lines
next;
} elsif (/^$/) { #Ignore blank lines
next;
} elsif ((/^\t(.*)$/) and ($previousField)) { #Add to Previous Field
if ($hashRef->{$previousField}) {
$hashRef->{$previousField} .= "\n$1";
} else {
$hashRef->{$previousField} = $1;
}
} elsif (/([^:]+):\t(.+)$/) {
$hashRef->{$1} = $2;
$fieldCount++;
undef($previousField);
} elsif (/([^:]+):$/) {
$previousField = $1;
$fieldCount++;
}
else {
if ($die) {
croak qq(Line #$numOfLines in $textFile is invalid\n);
} else {
$error = 2;
$errorMsg = qq(Line #$numOfLines in ) .
qq($textFile is invalid\n);
return 0;
}
}
}
my $returnValue = $fieldCount;
close (P4CMD);
if ($returnValue) {
return $returnValue;
} else {
$error = 3;
$errorMsg = "Command didn't execute!";
return 0;
}
}
#
########################################################################
########################################################################
# SUBROUTINE FORM DELETE
#
=pod
=head2 Subroutine formDelete
Purpose:
To delete form from Perforce database
=head3 Call:
C<formDelete("$p4Cmd","$id");>
=head4 Parameters Used:
=over 4
=item $p4Cmd
Perforce Form Type Command (job, client, change, etc)
=item $id
Id of that form type (job #, change #, user Id, etc)
=back
=head4 Returns:
=over 4
=item On Success
Non-Zero Value
=item On Failure
Zero
=back
=cut
#
########################################################################
########################################################################
#
#
sub formDelete {
my ($p4Cmd, $cmdId) = @_;
unless (loggedIn()) {
return 0; #$FormInfo::error already set
}
my $error = system(qq(p4 $p4Cmd -d $cmdId));
if ($error) {
if ($die) {
croak qq(Cannot execute command "p4 $p4Cmd -d $cmdId"\n);
} else {
$error = 1;
$errorMsg = qq(Cannot execute command ) .
qq("p4 $p4Cmd -d $cmdId"\n);
return 0;
}
}
return 1;
}
#
########################################################################
=pod
=head1 Package Variables
You can set use the following package variables
=head2 C<$FormInfo::debug>
Debugging level for module. Default is "0" for no debugging.
=head2 C<$FormInfo::die>
Whether to die on errors. Default is "1" which means dies on errors.
Set to "0" to not die on errors. Return values of "0" means an error.
Check $FormInfo::error for reason program died, and $FormInfo::errorMsg
for a text message explanation.
B<WARNING:> If you set this value to zero, you are responsible to
checking the return codes of all functions! Your program will continue
to execute even if it fails.
=head2 C<$FormInfo::error>
Error code returned. "1" means I<p4> command failed to execute.
=over 4
=item Return Value of 1
I<p4> command failed to execute.
=item Return Value of 2
I<textFormRead> detected that the text file you
were processing had an incorrect number of lines.
=item Return Value of 4
You are not logged into Perforce
=back
I<This value is only set if you've set $FormInfo::die = 0.>
=head2 $FormInfo::errorMsg
The text error message returned by $FormInfo::error
I<This value is only set if you've set $FormInfo::die = 0.>
=cut
#
########################################################################
1;