The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
# $Revision: 709 $$Date: 2005-05-03 17:32:07 -0400 (Tue, 03 May 2005) $$Author: wsnyder $
# Author: Wilson Snyder <wsnyder@wsnyder.org>
######################################################################
#
# Copyright 2002-2005 by Wilson Snyder.  This program is free software;
# you can redistribute it and/or modify it under the terms of either the GNU
# General Public License or the Perl Artistic License.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
######################################################################

package P4::C4::Ignore;
require 5.006_001;

use strict;
use vars qw($VERSION $Debug);
use Carp;
use File::Spec;
use File::Spec::Functions;
use IO::File;
use Cwd qw(getcwd);

######################################################################
#### Configuration Section

our $VERSION = '2.041';

#######################################################################
#######################################################################
#######################################################################

sub new {
    @_ >= 1 or croak 'usage: P4::C4::Ignore->new ({options})';
    my $class = shift || __PACKAGE__;		# Class (Getopt Element)
    my $self = {filename=>'.cvsignore',
		_files=>{},		# Cache by filename of raw text ignores
		_regexp=>{},		# Cache by filename of parsed ignores
		@_,
	    };
    bless $self, $class;
    $self->_addIgnore ('GLOBAL',(
				 '*~',	  '#*',    '.#*',    ',*',	   '_$*',   '*$',
				 '*.old', '*.bak', '*.BAK',  '*.orig', '*.rej', '.del-*',
				 '*.a',  '*.olb', '*.o',    '*.obj',  '*.so',  '*.exe',
				 '*.Z',  '*.elc', '*.ln',
				 'tags',	'TAGS',
				 '.make.state',	'.nse_depinfo',
				 'core',
				 # Not in CVS
				 '.dependency-info',
				 # Our own temp files
				 '.c4cache',	'.p4config',));
    
    # Read user's .cvsignore into global list
    $self->_readIgnore("GLOBAL",catfile($ENV{HOME},".cvsignore")) if defined $ENV{HOME};

    # Read CVSIGNORE environment
    $self->_addIgnore ("GLOBAL", (split /\s+/, $ENV{CVSIGNORE})) if defined $ENV{CVSIGNORE};

    return $self;
}

#######################################################################
# User methods

sub isIgnored {
    my $self = shift;
    my $filename = shift;

    $filename = File::Spec->rel2abs($filename);
    my @dirlist = File::Spec->splitdir($filename);
    while ($#dirlist > 0) {
	$filename = pop @dirlist;
	return 1 if _checkOneDir($self,catdir(@dirlist),$filename);
    }
    return 0;
}

#######################################################################
# Checking function

sub _checkOneDir {
    my $self = shift;
    my $dirname = shift;
    my $filename = shift;

    if (!$self->{_regexp}{$dirname}) {
	$self->_readIgnore($dirname, catfile($dirname,$self->{filename}));
    }

    my $basename = (File::Spec->splitpath($filename))[2];
    #print "CK1 $dirname $basename\n";
    foreach my $re (@{$self->{_regexp}{$dirname}}) {
	#print "CK1b $dirname $basename $re\n";
	return 1 if ($basename =~ /$re/);
    }
    return 0;
}

#######################################################################
# Reading functions

sub _readIgnore {
    my $self = shift;
    my $dirname = shift;
    my $filename = shift;

    return if $self->{_files}{$dirname} && $dirname ne 'GLOBAL';	# Cached
    my $fh = IO::File->new($filename,"r");
    $self->_addIgnore ($dirname, @{$self->{_files}{GLOBAL}}) if $dirname ne 'GLOBAL';
    if ($fh) {
	local $/; undef $/; my $wholefile = <$fh>;
	$self->_addIgnore ($dirname, (split /\s+/, $wholefile));
    }
}

sub _addIgnore {
    my $self = shift;
    my $dirname = shift;
    foreach my $re (@_) {
	print "  Ignore in $dirname: $re\n" if $Debug;
	if ($re eq "!") {
	    $self->{_files}{$dirname} = [];
	} else {
	    push @{$self->{_files}{$dirname}}, $re;
	}
    }

    # Convert patterns to regexp, for faster parsing
    my @relist = map { my $regexp = quotemeta $_;
		       $regexp =~ s%\\\*%.*%g;
		       $regexp =~ s%\\\?%.%g;
		       $regexp = "^".$regexp."\$";
		       qr/$regexp/;
		   } @{$self->{_files}{$dirname}};
    $self->{_regexp}{$dirname} = \@relist;
}

######################################################################
######################################################################
######################################################################
######################################################################
# Overrides

package P4::C4;
use strict;

sub ignoredFiles {
    my $self = shift;

    $self->{_ignore} = new P4::C4::Ignore() if !$self->{_ignore};
    foreach my $fref (values %{$self->{_files}}) {
	if ($fref->{clientMtime}
	    && !($fref->{headAction} && $fref->{headAction} ne 'delete')
	    && !$fref->{ignore}) {
	    #use Data::Dumper; print "check",Dumper($fref) if $fref->{filename} =~ /c4cache/;
	    $fref->{ignore} = $self->{_ignore}->isIgnored($fref->{filename});
	    $fref->{unknown} = 1 if !$fref->{ignore};
	}
    }
}

######################################################################
### Package return
1;
__END__

=pod

=head1 NAME

P4::C4::Ignore - Read a cvs ignore file

=head1 SYNOPSIS

  use P4::C4::Ignore;

  my $ign = new P4::C4::Ignore();
  $ign->isIgnored ($filename);

  ...

=head1 DESCRIPTION

The L<P4::C4::Ignore> package reads .cvsignore files and provides matching
functions.

=head1 IGNORE FILES

Ignore files are mostly compatible with CVS.  The global list of ignores is
initialized with:

    *~      #*      .#*     ,*      _$*     *$
    *.old   *.bak   *.BAK   *.orig  *.rej   .del-*
    *.a     *.olb   *.o     *.obj   *.so    *.exe
    *.Z     *.elc   *.ln
    .c4cache     .p4config
    .make.state  .nse_depinfo  .dependency-info
    tags         TAGS
    core

Patterns in the home directory file ~/.cvsignore, or the CVSIGNORE
environment variable are appended to this list.

Each directory may have a local '.cvsignore' file.  The patterns found in
local `.cvsignore' are only valid for the directory that contains them, not
for any sub-directories.  

In any of the places listed above, a single exclamation mark (`!')  clears
the ignore list.  This can be used if you want to store any file which
normally is ignored.

The wildcards * and ? are honored, no other wildcards are currently
supported.

=head1 METHODS

=over 4

=item $ign = P4::C4::Ignore->new ( I<opts> )

Create a new Ignore hash.  "filename=>I<filename>" may be specified to
override the default of .cvsignore for reading the ignore file.

Any .cvsignore files that are read are cached in this object to save time.
Thus if a .cvsignore file is being written by the application, a new object
will have to be created to clear the hash.

=item $self->is_ignored ( $file )

Returns true if the file is being ignored.

=back

=head1 SEE ALSO

L<P4::C4>

=head1 DISTRIBUTION

The latest version is available from CPAN.
The latest version is available from CPAN and from L<http://www.veripool.com/>.

Copyright 2002-2005 by Wilson Snyder.  This package is free software; you
can redistribute it and/or modify it under the terms of either the GNU
Lesser General Public License or the Perl Artistic License.

=head1 AUTHORS

Wilson Snyder <wsnyder@wsnyder.org>

=cut