FormInfo.pm #1

  • //
  • guest/
  • david_weintraub/
  • FormInfo.pm
  • View
  • Commits
  • Open Download .zip Download (16 KB)
#! /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;
# Change User Description Committed
#1 5710 David Weintraub Initial Stuff for my depot