#! /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 for inputing and outputing information for its commands. These functions help the user to use Perforce I 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 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 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 =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. 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 = ); 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 () { 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 to an array. Therefore, you need to test the return value of the field with a Perl I 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 Note that I can use the following Perforce commands that I 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), 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. 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 = ); 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 () { 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 =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 =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 and not a I. 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 () { $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 =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 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 command failed to execute. =over 4 =item Return Value of 1 I command failed to execute. =item Return Value of 2 I 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 =head2 $FormInfo::errorMsg The text error message returned by $FormInfo::error I =cut # ######################################################################## 1;