<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">package ExtUtils::Manifest;

=head1 NAME

ExtUtils::Manifest - utilities to write and check a MANIFEST file

=head1 SYNOPSIS

C&lt;require ExtUtils::Manifest;&gt;

C&lt;ExtUtils::Manifest::mkmanifest;&gt;

C&lt;ExtUtils::Manifest::manicheck;&gt;

C&lt;ExtUtils::Manifest::filecheck;&gt;

C&lt;ExtUtils::Manifest::fullcheck;&gt;

C&lt;ExtUtils::Manifest::maniread($file);&gt;

C&lt;ExtUtils::Manifest::manicopy($read,$target);&gt;

=head1 DESCRIPTION

Mkmanifest() writes all files in and below the current directory to a
file named C&lt;MANIFEST&gt; in the current directory. It works similar to

    find . -print

but in doing so checks each line in an existing C&lt;MANIFEST&gt; file and
includes any comments that are found in the existing C&lt;MANIFEST&gt; file
in the new one. Anything between white space and an end of line within
a C&lt;MANIFEST&gt; file is considered to be a comment. Filenames and
comments are seperated by one or more TAB characters in the
output. All files that match any regular expression in a file
C&lt;MANIFEST.SKIP&gt; (if such a file exists) are ignored.

Manicheck() checks if all the files within a C&lt;MANIFEST&gt; in the current
directory really do exist.

Filecheck() finds files below the current directory that are not
mentioned in the C&lt;MANIFEST&gt; file. An optional file C&lt;MANIFEST.SKIP&gt;
will be consulted. Any file matching a regular expression in such a
file will not be reported as missing in the C&lt;MANIFEST&gt; file.

Fullcheck() does both a manicheck() and a filecheck().

Maniread($file) reads a named C&lt;MANIFEST&gt; file (defaults to
C&lt;MANIFEST&gt; in the current directory) and returns a HASH reference
with files being the keys and comments being the values of the HASH.

I&lt;Manicopy($read,$target)&gt; copies the files that are the keys in the
HASH I&lt;%$read&gt; to the named target directory. The HASH reference
I&lt;$read&gt; is typically returned by the maniread() function. This
function is useful for producing a directory tree identical to the
intended distribution tree.

=head1 MANIFEST.SKIP

The file MANIFEST.SKIP may contain regular expressions of files that
should be ignored by mkmanifest() and filecheck(). The regular
expressions should appear one on each line. A typical example:

    \bRCS\b
    ^MANIFEST\.
    ^Makefile$
    ~$
    \.html$
    \.old$
    ^blib/
    ^MakeMaker-\d

=head1 EXPORT_OK

C&lt;&amp;mkmanifest&gt;, C&lt;&amp;manicheck&gt;, C&lt;&amp;filecheck&gt;, C&lt;&amp;fullcheck&gt;,
C&lt;&amp;maniread&gt;, and C&lt;&amp;manicopy&gt; are exportable.

=head1 DIAGNOSTICS

All diagnostic output is sent to C&lt;STDERR&gt;.

=over
    
=item C&lt;Not in MANIFEST:&gt; I&lt;file&gt;
is reported if a file is found, that is missing in the C&lt;MANIFEST&gt;
file which is excluded by a regular expression in the file
C&lt;MANIFEST.SKIP&gt;.

=item C&lt;No such file:&gt; I&lt;file&gt;
is reported if a file mentioned in a C&lt;MANIFEST&gt; file does not
exist.

=item C&lt;MANIFEST:&gt; I&lt;$!&gt;
is reported if C&lt;MANIFEST&gt; could not be opened.

=item C&lt;Added to MANIFEST:&gt; I&lt;file&gt;
is reported by mkmanifest() if $Verbose is set and a file is added
to MANIFEST. $Verbose is set to 1 by default.

=back

=head1 AUTHOR

Andreas Koenig F&lt;E&lt;lt&gt;koenig@franz.ww.TU-Berlin.DEE&lt;gt&gt;&gt;

=cut

require Exporter;
@ISA=('Exporter');
@EXPORT_OK = ('mkmanifest', 'manicheck', 'fullcheck', 'filecheck', 
	      'maniread', 'manicopy');

use File::Find;
use Carp;

$Debug = 0;
$Verbose = 1;

($Version) = sprintf("%d.%02d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+)/);
$Version = $Version; #avoid warning

$Quiet = 0;

sub mkmanifest {
    my $manimiss = 0;
    my $read = maniread() or $manimiss++;
    $read = {} if $manimiss;
    my $matches = _maniskip();
    my $found = manifind();
    my($key,$val,$file,%all);
    my %all = (%$found, %$read);
    local *M;
    rename "MANIFEST", "MANIFEST.bak" unless $manimiss;
    open M, "&gt;MANIFEST" or die "Could not open MANIFEST: $!";
    foreach $file (sort keys %all) {
	next if &amp;$matches($file);
	if ($Verbose){
	    warn "Added to MANIFEST: $file\n" unless exists $read-&gt;{$file};
	}
	my $tabs = (5 - (length($file)+1)/8);
	$tabs = 1 if $tabs &lt; 1;
	$tabs = 0 unless $all{$file};
	print M $file, "\t" x $tabs, $all{$file}, "\n";
    }
    close M;
}

sub manifind {
    local $found = {};
    find(sub {return if -d $_;
	      (my $name = $File::Find::name) =~ s|./||;
	      warn "Debug: diskfile $name\n" if $Debug;
	      $found-&gt;{$name} = "";}, ".");
    $found;
}

sub fullcheck {
    _manicheck(3);
}

sub manicheck {
    return @{(_manicheck(1))[0]};
}

sub filecheck {
    return @{(_manicheck(2))[1]};
}

sub _manicheck {
    my($arg) = @_;
    my $read = maniread();
    my $file;
    my(@missfile,@missentry);
    if ($arg &amp; 1){
	my $found = manifind();
	foreach $file (sort keys %$read){
	    warn "Debug: manicheck checking from MANIFEST $file\n" if $Debug;
	    unless ( exists $found-&gt;{$file} ) {
	      warn "No such file: $file\n" unless $Quiet;
	      push @missfile, $file;
	    }
	}
    }
    if ($arg &amp; 2){
	$read ||= {};
	my $matches = _maniskip();
	my $found = manifind();
	foreach $file (sort keys %$found){
	    next if &amp;$matches($file);
	    warn "Debug: manicheck checking from disk $file\n" if $Debug;
	    unless ( exists $read-&gt;{$file} ) {
	      warn "Not in MANIFEST: $file\n" unless $Quiet;
	      push @missentry, $file;
	    }
	}
    }
    (\@missfile,\@missentry);
}

sub maniread {
    my ($mfile) = @_;
    $mfile = "MANIFEST" unless defined $mfile;
    my $read = {};
    local *M;
    unless (open M, $mfile){
	warn "$mfile: $!";
	return $read;
    }
    while (&lt;M&gt;){
	chomp;
	/^(\S+)\s*(.*)/ and $read-&gt;{$1}=$2;
    }
    close M;
    $read;
}

# returns an anonymous sub that decides if an argument matches
sub _maniskip {
    my ($mfile) = @_;
    my $matches = sub {0};
    my @skip ;
    my $mfile = "MANIFEST.SKIP" unless defined $mfile;
    local *M;
    return $matches unless -f $mfile;
    open M, $mfile or return $matches;
    while (&lt;M&gt;){
	chomp;
	next if /^\s*$/;
	push @skip, $_;
    }
    close M;
    my $sub = "\$matches = "
	. "sub { my(\$arg)=\@_; return 1 if "
	. join (" || ",  (map {s!/!\\/!g; "\$arg =~ m/$_/o "} @skip), 0)
	. " }";
    eval $sub;
    print "Debug: $sub\n" if $Debug;
    $matches;
}

sub manicopy {
    my($read,$target)=@_;
    croak "manicopy() called without target argument" unless defined $target;
    require File::Path;
    require File::Basename;
    my(%dirs,$file);
    foreach $file (keys %$read){
	my $dir = File::Basename::dirname($file);
	File::Path::mkpath("$target/$dir");
	cp_if_diff($file, "$target/$file");
    }
}

sub cp_if_diff {
    my($from,$to)=@_;
    -f $from || carp "$0: $from not found";
    system "cmp", "-s", $from, $to;
    if ($?) {
	unlink($to);   # In case we don't have write permissions.
	(system 'cp', $from, $to) == 0 or confess "system 'cp': $!";
    }
}

1;
</pre></body></html>