############################################################################### # CLASS Clearcase::LsCmd # This class inherits from the CCCmpdProc and executes the cleartool command # "ls" with args "-recurse -long" to list all files in cwd. # # It overrider the CCProcess member to process the resultant output from the ls # command and store information about all files/dirs in and below the pwd. # # It uses the CCIgnore class to determine if a file should be ignored or included # in the processing of the cmd output. # # It stores as a result of the command for each file/dir # NAME = The full path of the file (as defined by CCCmdProc) # STATE = File NAME State (CHECKEDOUT, CHECKIN, PRIVATE) # VERSION = If state=CHECKEDIN Then current version of NAME in view # If state=CHECKEDOUT Then version of NAME that the checkout was from # ANNOTATION= Any Special Annotations returned by ls on NAME ############################################################################### package Clearcase::LsCmd; use strict; use Cwd qw/ abs_path /; use DeployUtils::Logger; use Clearcase::IgnoreList; use Clearcase::CmdProc; use vars qw( @ISA ); @ISA = qw( Clearcase::CmdProc ); # Globally defined states our $StateCheckedIn = "CHECKEDIN"; our $StateCheckedOut = "CHECKEDOUT"; our $StatePrivate = "PRIVATE"; #============================================================================== # Constructor #============================================================================== sub new { my $obclass = shift; my $class = ref($obclass) || $obclass; my $dir = shift; LogDebug("Clearcase::LsCmd::new Instantiating new object of class $class"); # if no dir of dir is "." the get full absolute path, abs_path returns path with "/" regardless of platform $dir = abs_path(".") if ( ! defined($dir) || $dir eq "." ); # however if passed in make sure we change \ to / $dir =~ s|\\|/|g; # Call base class's constructor my $self = $class->SUPER::new(); # Add this class's data to the class object $self->{_FINDDIR} = $dir; $self->{_IGNOREFILES} = 1; $self->{_IGNORECOUNT} = 0; #indexes used # State index is a hash of hashes of arrays. The contents of _STATE are hashes keyed on global states # These hashes contain an array of element numbers into the FILEINFO array # The branch & annotation are similar but keyed on branches and annotations $self->{_STATE} = {}; $self->{_BRANCH} = {}; $self->{_ANNOTATION} = {}; bless($self, $class); # set Clearcase command and arg $self->CCcmd("ls"); $self->CCargs("-long"); return $self; } # new #============================================================================== # ignoreFiles # Sets & returns the ignore files variable that determines whether to use the # IgnoreFiles class for files to ignore #============================================================================== sub ignoreFiles { my $self = shift; if ( @_ ) { $self->{_IGNOREFILES} = shift; LogDebug("ClearCase::LsCmd::ignoreFiles Set to $self->{_IGNOREFILES}"); } return $self->{_IGNOREFILES}; } # ignoreFiles #============================================================================== # CCexecute # simply adds dir to args and runs base class CCexecute #============================================================================== sub CCexecute { my $self = shift; my $args = $self->CCargs(); # search directory first $self->CCargs("$args -directory $self->{_FINDDIR}"); $self->SUPER::CCexecute(); # now serach the contents $self->CCargs("$args -recurse $self->{_FINDDIR}"); $self->SUPER::CCexecute(); } # CCexecute #============================================================================== # CCprocess overrides base class's CCProcess to process result #============================================================================== sub CCprocess { my $self = shift; my $line; my ($name, $state, $version, $branch, $annotation ) = ( "", "", "", "", "" ); my $index; my $dirRE = $self->{_FINDDIR}; # Change the search RE for the findDir so that all occurances of "/" in path are changed to "[/\\]" # to match either type of path separator $dirRE =~ s|/|\[/\\\\\]|g; NEXTLINE: while ( $line = $self->getline() ) { chomp $line; $line =~ s/\r//g; # remove carraige returns # ignore blank lines or lines with directory ".*" with quotes next NEXTLINE if ( $line =~ /^\s*$/ || $line =~ /directory\s*\".*\":/ ); if ( $line =~ /^view private object\s*(.*)\s*$/ ) { $name = $1; $state = $StatePrivate; $version = ""; $branch = ""; $annotation = ""; if ( $self->{_IGNOREFILES} && Clearcase::IgnoreList->ignoreFile($name) ) { LogDebug("Clearcase::LsCmd::CCProcess Ignoring Private file $name"); $self->{_IGNORECOUNT}++; next NEXTLINE; } } elsif ( $line =~ /^(?:directory |)version\s*(.*)\@\@(.*)$/ ) { # got the file name, & rest of line $name = $1; $line = $2; # remove the Rule: from the end of the line as we dont need it $line =~ s/\s*Rule: .*$//; # Check for annotations & store, removing from line $line =~ s/\s*\[(.*)\]\s*$//; if ( defined($1) ) { $annotation = $1; } else { $annotation = ""; } # See if Checked out state if ( $line =~ /^.*CHECKEDOUT from (.*)$/ ) { $state = $StateCheckedOut; $version = $1; # version is version checked out from } else { $state = $StateCheckedIn; $version = $line; } $branch = $version; # remove version number and dir separator from version to get branch $branch =~ s|[/\\]\d*$||; } else { LogError("-x", "Clearcase::LsCmd::CCprocess Unknown Input Line as follows"); LogError("$line"); } # Remove findDir from path if find dir is same as pwd, makes it less clutered $name =~ s|$dirRE|\.| if ( $self->CCcwd() =~ m|^$dirRE| ); # Add name to file list and set it as current element $index = $self->addToFileList($name); # now add other elements, the index is remembered in the base class $self->updateCurrentFile("STATE", $state, "VERSION", $version, "ANNOTATION", $annotation); # update indexes push(@{$self->{_STATE}{$state}}, $index); push(@{$self->{_BRANCH}{$branch}}, $index) if ( $branch ne "" ); push(@{$self->{_ANNOTATION}{$annotation}}, $index) if ( $annotation ne "" ); } } # process #============================================================================== # getNumByState - Returns the number of items found by state #============================================================================== sub getNumByState { my $self = shift; my $state = shift; my $retval; if ( $state eq $StateCheckedIn ) { $retval = $#{$self->{_STATE}{$StateCheckedIn}} + 1; } elsif ( $state eq $StateCheckedOut ) { $retval = $#{$self->{_STATE}{$StateCheckedOut}} + 1; } elsif ( $state eq $StatePrivate ) { $retval = $#{$self->{_STATE}{$StatePrivate}} + 1; } else { LogError("Clearcase::LsCmd::getNumByState Invalid State supplied"); } return $retval; } # getNumByState #============================================================================== # getListByState - Returns a reference to an array of files by state #============================================================================== sub getListByState { my $self = shift; my $state = shift; my @retval; if ( $state eq $StateCheckedIn ) { foreach my $index ( @{$self->{_STATE}{$StateCheckedIn}} ) { push(@retval, $self->getElement($index)->{NAME}); } } elsif ( $state eq $StateCheckedOut ) { foreach my $index ( @{$self->{_STATE}{$StateCheckedOut}} ) { push(@retval, $self->getElement($index)->{NAME}); } } elsif ( $state eq $StatePrivate ) { foreach my $index ( @{$self->{_STATE}{$StatePrivate}} ) { push(@retval, $self->getElement($index)->{NAME}); } } else { LogError("Clearcase::LsCmd::getListByState Invalid State supplied"); } return \@retval; } # getListByState #============================================================================== # getNumBranches - Returns number of branches found #============================================================================== sub getNumBranches { my $self = shift; return scalar keys %{$self->{_BRANCH}}; } # getNumBranches #============================================================================== # getListBranches - Returns a reference to an array of branches found #============================================================================== sub getListBranches { my $self = shift; my @retval; @retval = keys %{$self->{_BRANCH}}; return \@retval; } # getListBranches #============================================================================== # getNumFilesByBranch - Returns the number of files associated to the supplied branch #============================================================================== sub getNumFilesByBranch { my $self = shift; my $branch = shift; my $retval = undef; if ( defined($branch) && defined($self->{_BRANCH}{$branch}) ) { $retval = $#{$self->{_BRANCH}{$branch}} + 1; } return $retval; } # getNumFilesByBranch #============================================================================== # getListFilesByBranch - Returns a reference to an array of files found in the # supplied branch #============================================================================== sub getListFilesByBranch { my $self = shift; my $branch = shift; my @retval; if ( defined($branch) && defined($self->{_BRANCH}{$branch}) ) { foreach my $index ( @{$self->{_BRANCH}{$branch}} ) { push(@retval, $self->getElement($index)->{NAME}); } } return \@retval; } # getListFilesByBranch #============================================================================== # getNumAnnotations - Returns number of Annotations found #============================================================================== sub getNumAnnotations { my $self = shift; return scalar keys %{$self->{_ANNOTATION}}; } # getNumAnnotations #============================================================================== # getListAnnotations - Returns a reference to an array of Annotations found #============================================================================== sub getListAnnotations { my $self = shift; my @retval; @retval = keys %{$self->{_ANNOTATION}}; return \@retval; } # getListAnnotations #============================================================================== # getNumFilesByAnnotation - Returns the number of files associated to the supplied Annotation # If supplied is all then it returns the total number of files with annotations #============================================================================== sub getNumFilesByAnnotation { my $self = shift; my $annotation = shift; my $retval = undef; if ( defined($annotation) ) { if ( $annotation =~ /all/i ) { $retval = 0; foreach my $annot ( keys %{$self->{_ANNOTATION}} ) { $retval += $#{$self->{_ANNOTATION}{$annot}} + 1 } } if ( defined($self->{_ANNOTATION}{$annotation}) ) { $retval = $#{$self->{_ANNOTATION}{$annotation}} + 1; } } return $retval; } # getNumFilesByAnnotation #============================================================================== # getListFilesByAnnotation - Returns a reference to an array of files found # with the supplied Annotation #============================================================================== sub getListFilesByAnnotation { my $self = shift; my $annotation = shift; my @retval; if ( defined($annotation) && defined($self->{_ANNOTATION}{$annotation}) ) { foreach my $index ( @{$self->{_ANNOTATION}{$annotation}} ) { push(@retval, $self->getElement($index)->{NAME}); } } return \@retval; } # getListFilesByAnnotation #============================================================================== # getNumIgnored # Returns the number of files ignored. #============================================================================== sub getNumIgnored { my $self = shift; return $self->{_IGNORECOUNT}; } # getNumIgnored 1;