xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/ExtUtils/Manifest.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gatepackage ExtUtils::Manifest;
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gaterequire Exporter;
4*0Sstevel@tonic-gateuse Config;
5*0Sstevel@tonic-gateuse File::Find;
6*0Sstevel@tonic-gateuse File::Copy 'copy';
7*0Sstevel@tonic-gateuse File::Spec;
8*0Sstevel@tonic-gateuse Carp;
9*0Sstevel@tonic-gateuse strict;
10*0Sstevel@tonic-gate
11*0Sstevel@tonic-gateuse vars qw($VERSION @ISA @EXPORT_OK
12*0Sstevel@tonic-gate          $Is_MacOS $Is_VMS
13*0Sstevel@tonic-gate          $Debug $Verbose $Quiet $MANIFEST $DEFAULT_MSKIP);
14*0Sstevel@tonic-gate
15*0Sstevel@tonic-gate$VERSION = 1.42;
16*0Sstevel@tonic-gate@ISA=('Exporter');
17*0Sstevel@tonic-gate@EXPORT_OK = qw(mkmanifest
18*0Sstevel@tonic-gate                manicheck  filecheck  fullcheck  skipcheck
19*0Sstevel@tonic-gate                manifind   maniread   manicopy   maniadd
20*0Sstevel@tonic-gate               );
21*0Sstevel@tonic-gate
22*0Sstevel@tonic-gate$Is_MacOS = $^O eq 'MacOS';
23*0Sstevel@tonic-gate$Is_VMS   = $^O eq 'VMS';
24*0Sstevel@tonic-gaterequire VMS::Filespec if $Is_VMS;
25*0Sstevel@tonic-gate
26*0Sstevel@tonic-gate$Debug   = $ENV{PERL_MM_MANIFEST_DEBUG} || 0;
27*0Sstevel@tonic-gate$Verbose = defined $ENV{PERL_MM_MANIFEST_VERBOSE} ?
28*0Sstevel@tonic-gate                   $ENV{PERL_MM_MANIFEST_VERBOSE} : 1;
29*0Sstevel@tonic-gate$Quiet = 0;
30*0Sstevel@tonic-gate$MANIFEST = 'MANIFEST';
31*0Sstevel@tonic-gate
32*0Sstevel@tonic-gatemy $Filename = __FILE__;
33*0Sstevel@tonic-gate$DEFAULT_MSKIP = (File::Spec->splitpath($Filename))[1].
34*0Sstevel@tonic-gate                 "$MANIFEST.SKIP";
35*0Sstevel@tonic-gate
36*0Sstevel@tonic-gate
37*0Sstevel@tonic-gate=head1 NAME
38*0Sstevel@tonic-gate
39*0Sstevel@tonic-gateExtUtils::Manifest - utilities to write and check a MANIFEST file
40*0Sstevel@tonic-gate
41*0Sstevel@tonic-gate=head1 SYNOPSIS
42*0Sstevel@tonic-gate
43*0Sstevel@tonic-gate    use ExtUtils::Manifest qw(...funcs to import...);
44*0Sstevel@tonic-gate
45*0Sstevel@tonic-gate    mkmanifest();
46*0Sstevel@tonic-gate
47*0Sstevel@tonic-gate    my @missing_files    = manicheck;
48*0Sstevel@tonic-gate    my @skipped          = skipcheck;
49*0Sstevel@tonic-gate    my @extra_files      = filecheck;
50*0Sstevel@tonic-gate    my($missing, $extra) = fullcheck;
51*0Sstevel@tonic-gate
52*0Sstevel@tonic-gate    my $found    = manifind();
53*0Sstevel@tonic-gate
54*0Sstevel@tonic-gate    my $manifest = maniread();
55*0Sstevel@tonic-gate
56*0Sstevel@tonic-gate    manicopy($read,$target);
57*0Sstevel@tonic-gate
58*0Sstevel@tonic-gate    maniadd({$file => $comment, ...});
59*0Sstevel@tonic-gate
60*0Sstevel@tonic-gate
61*0Sstevel@tonic-gate=head1 DESCRIPTION
62*0Sstevel@tonic-gate
63*0Sstevel@tonic-gate=head2 Functions
64*0Sstevel@tonic-gate
65*0Sstevel@tonic-gateExtUtils::Manifest exports no functions by default.  The following are
66*0Sstevel@tonic-gateexported on request
67*0Sstevel@tonic-gate
68*0Sstevel@tonic-gate=over 4
69*0Sstevel@tonic-gate
70*0Sstevel@tonic-gate=item mkmanifest
71*0Sstevel@tonic-gate
72*0Sstevel@tonic-gate    mkmanifest();
73*0Sstevel@tonic-gate
74*0Sstevel@tonic-gateWrites all files in and below the current directory to your F<MANIFEST>.
75*0Sstevel@tonic-gateIt works similar to
76*0Sstevel@tonic-gate
77*0Sstevel@tonic-gate    find . > MANIFEST
78*0Sstevel@tonic-gate
79*0Sstevel@tonic-gateAll files that match any regular expression in a file F<MANIFEST.SKIP>
80*0Sstevel@tonic-gate(if it exists) are ignored.
81*0Sstevel@tonic-gate
82*0Sstevel@tonic-gateAny existing F<MANIFEST> file will be saved as F<MANIFEST.bak>.  Lines
83*0Sstevel@tonic-gatefrom the old F<MANIFEST> file is preserved, including any comments
84*0Sstevel@tonic-gatethat are found in the existing F<MANIFEST> file in the new one.
85*0Sstevel@tonic-gate
86*0Sstevel@tonic-gate=cut
87*0Sstevel@tonic-gate
88*0Sstevel@tonic-gatesub _sort {
89*0Sstevel@tonic-gate    return sort { lc $a cmp lc $b } @_;
90*0Sstevel@tonic-gate}
91*0Sstevel@tonic-gate
92*0Sstevel@tonic-gatesub mkmanifest {
93*0Sstevel@tonic-gate    my $manimiss = 0;
94*0Sstevel@tonic-gate    my $read = (-r 'MANIFEST' && maniread()) or $manimiss++;
95*0Sstevel@tonic-gate    $read = {} if $manimiss;
96*0Sstevel@tonic-gate    local *M;
97*0Sstevel@tonic-gate    rename $MANIFEST, "$MANIFEST.bak" unless $manimiss;
98*0Sstevel@tonic-gate    open M, ">$MANIFEST" or die "Could not open $MANIFEST: $!";
99*0Sstevel@tonic-gate    my $skip = _maniskip();
100*0Sstevel@tonic-gate    my $found = manifind();
101*0Sstevel@tonic-gate    my($key,$val,$file,%all);
102*0Sstevel@tonic-gate    %all = (%$found, %$read);
103*0Sstevel@tonic-gate    $all{$MANIFEST} = ($Is_VMS ? "$MANIFEST\t\t" : '') . 'This list of files'
104*0Sstevel@tonic-gate        if $manimiss; # add new MANIFEST to known file list
105*0Sstevel@tonic-gate    foreach $file (_sort keys %all) {
106*0Sstevel@tonic-gate	if ($skip->($file)) {
107*0Sstevel@tonic-gate	    # Policy: only remove files if they're listed in MANIFEST.SKIP.
108*0Sstevel@tonic-gate	    # Don't remove files just because they don't exist.
109*0Sstevel@tonic-gate	    warn "Removed from $MANIFEST: $file\n" if $Verbose and exists $read->{$file};
110*0Sstevel@tonic-gate	    next;
111*0Sstevel@tonic-gate	}
112*0Sstevel@tonic-gate	if ($Verbose){
113*0Sstevel@tonic-gate	    warn "Added to $MANIFEST: $file\n" unless exists $read->{$file};
114*0Sstevel@tonic-gate	}
115*0Sstevel@tonic-gate	my $text = $all{$file};
116*0Sstevel@tonic-gate	($file,$text) = split(/\s+/,$text,2) if $Is_VMS && $text;
117*0Sstevel@tonic-gate	$file = _unmacify($file);
118*0Sstevel@tonic-gate	my $tabs = (5 - (length($file)+1)/8);
119*0Sstevel@tonic-gate	$tabs = 1 if $tabs < 1;
120*0Sstevel@tonic-gate	$tabs = 0 unless $text;
121*0Sstevel@tonic-gate	print M $file, "\t" x $tabs, $text, "\n";
122*0Sstevel@tonic-gate    }
123*0Sstevel@tonic-gate    close M;
124*0Sstevel@tonic-gate}
125*0Sstevel@tonic-gate
126*0Sstevel@tonic-gate# Geez, shouldn't this use File::Spec or File::Basename or something?
127*0Sstevel@tonic-gate# Why so careful about dependencies?
128*0Sstevel@tonic-gatesub clean_up_filename {
129*0Sstevel@tonic-gate  my $filename = shift;
130*0Sstevel@tonic-gate  $filename =~ s|^\./||;
131*0Sstevel@tonic-gate  $filename =~ s/^:([^:]+)$/$1/ if $Is_MacOS;
132*0Sstevel@tonic-gate  return $filename;
133*0Sstevel@tonic-gate}
134*0Sstevel@tonic-gate
135*0Sstevel@tonic-gate
136*0Sstevel@tonic-gate=item manifind
137*0Sstevel@tonic-gate
138*0Sstevel@tonic-gate    my $found = manifind();
139*0Sstevel@tonic-gate
140*0Sstevel@tonic-gatereturns a hash reference. The keys of the hash are the files found
141*0Sstevel@tonic-gatebelow the current directory.
142*0Sstevel@tonic-gate
143*0Sstevel@tonic-gate=cut
144*0Sstevel@tonic-gate
145*0Sstevel@tonic-gatesub manifind {
146*0Sstevel@tonic-gate    my $p = shift || {};
147*0Sstevel@tonic-gate    my $found = {};
148*0Sstevel@tonic-gate
149*0Sstevel@tonic-gate    my $wanted = sub {
150*0Sstevel@tonic-gate	my $name = clean_up_filename($File::Find::name);
151*0Sstevel@tonic-gate	warn "Debug: diskfile $name\n" if $Debug;
152*0Sstevel@tonic-gate	return if -d $_;
153*0Sstevel@tonic-gate
154*0Sstevel@tonic-gate        if( $Is_VMS ) {
155*0Sstevel@tonic-gate            $name =~ s#(.*)\.$#\L$1#;
156*0Sstevel@tonic-gate            $name = uc($name) if $name =~ /^MANIFEST(\.SKIP)?$/i;
157*0Sstevel@tonic-gate        }
158*0Sstevel@tonic-gate	$found->{$name} = "";
159*0Sstevel@tonic-gate    };
160*0Sstevel@tonic-gate
161*0Sstevel@tonic-gate    # We have to use "$File::Find::dir/$_" in preprocess, because
162*0Sstevel@tonic-gate    # $File::Find::name is unavailable.
163*0Sstevel@tonic-gate    # Also, it's okay to use / here, because MANIFEST files use Unix-style
164*0Sstevel@tonic-gate    # paths.
165*0Sstevel@tonic-gate    find({wanted => $wanted},
166*0Sstevel@tonic-gate	 $Is_MacOS ? ":" : ".");
167*0Sstevel@tonic-gate
168*0Sstevel@tonic-gate    return $found;
169*0Sstevel@tonic-gate}
170*0Sstevel@tonic-gate
171*0Sstevel@tonic-gate
172*0Sstevel@tonic-gate=item manicheck
173*0Sstevel@tonic-gate
174*0Sstevel@tonic-gate    my @missing_files = manicheck();
175*0Sstevel@tonic-gate
176*0Sstevel@tonic-gatechecks if all the files within a C<MANIFEST> in the current directory
177*0Sstevel@tonic-gatereally do exist. If C<MANIFEST> and the tree below the current
178*0Sstevel@tonic-gatedirectory are in sync it silently returns an empty list.
179*0Sstevel@tonic-gateOtherwise it returns a list of files which are listed in the
180*0Sstevel@tonic-gateC<MANIFEST> but missing from the directory, and by default also
181*0Sstevel@tonic-gateoutputs these names to STDERR.
182*0Sstevel@tonic-gate
183*0Sstevel@tonic-gate=cut
184*0Sstevel@tonic-gate
185*0Sstevel@tonic-gatesub manicheck {
186*0Sstevel@tonic-gate    return _check_files();
187*0Sstevel@tonic-gate}
188*0Sstevel@tonic-gate
189*0Sstevel@tonic-gate
190*0Sstevel@tonic-gate=item filecheck
191*0Sstevel@tonic-gate
192*0Sstevel@tonic-gate    my @extra_files = filecheck();
193*0Sstevel@tonic-gate
194*0Sstevel@tonic-gatefinds files below the current directory that are not mentioned in the
195*0Sstevel@tonic-gateC<MANIFEST> file. An optional file C<MANIFEST.SKIP> will be
196*0Sstevel@tonic-gateconsulted. Any file matching a regular expression in such a file will
197*0Sstevel@tonic-gatenot be reported as missing in the C<MANIFEST> file. The list of any
198*0Sstevel@tonic-gateextraneous files found is returned, and by default also reported to
199*0Sstevel@tonic-gateSTDERR.
200*0Sstevel@tonic-gate
201*0Sstevel@tonic-gate=cut
202*0Sstevel@tonic-gate
203*0Sstevel@tonic-gatesub filecheck {
204*0Sstevel@tonic-gate    return _check_manifest();
205*0Sstevel@tonic-gate}
206*0Sstevel@tonic-gate
207*0Sstevel@tonic-gate
208*0Sstevel@tonic-gate=item fullcheck
209*0Sstevel@tonic-gate
210*0Sstevel@tonic-gate    my($missing, $extra) = fullcheck();
211*0Sstevel@tonic-gate
212*0Sstevel@tonic-gatedoes both a manicheck() and a filecheck(), returning then as two array
213*0Sstevel@tonic-gaterefs.
214*0Sstevel@tonic-gate
215*0Sstevel@tonic-gate=cut
216*0Sstevel@tonic-gate
217*0Sstevel@tonic-gatesub fullcheck {
218*0Sstevel@tonic-gate    return [_check_files()], [_check_manifest()];
219*0Sstevel@tonic-gate}
220*0Sstevel@tonic-gate
221*0Sstevel@tonic-gate
222*0Sstevel@tonic-gate=item skipcheck
223*0Sstevel@tonic-gate
224*0Sstevel@tonic-gate    my @skipped = skipcheck();
225*0Sstevel@tonic-gate
226*0Sstevel@tonic-gatelists all the files that are skipped due to your C<MANIFEST.SKIP>
227*0Sstevel@tonic-gatefile.
228*0Sstevel@tonic-gate
229*0Sstevel@tonic-gate=cut
230*0Sstevel@tonic-gate
231*0Sstevel@tonic-gatesub skipcheck {
232*0Sstevel@tonic-gate    my($p) = @_;
233*0Sstevel@tonic-gate    my $found = manifind();
234*0Sstevel@tonic-gate    my $matches = _maniskip();
235*0Sstevel@tonic-gate
236*0Sstevel@tonic-gate    my @skipped = ();
237*0Sstevel@tonic-gate    foreach my $file (_sort keys %$found){
238*0Sstevel@tonic-gate        if (&$matches($file)){
239*0Sstevel@tonic-gate            warn "Skipping $file\n";
240*0Sstevel@tonic-gate            push @skipped, $file;
241*0Sstevel@tonic-gate            next;
242*0Sstevel@tonic-gate        }
243*0Sstevel@tonic-gate    }
244*0Sstevel@tonic-gate
245*0Sstevel@tonic-gate    return @skipped;
246*0Sstevel@tonic-gate}
247*0Sstevel@tonic-gate
248*0Sstevel@tonic-gate
249*0Sstevel@tonic-gatesub _check_files {
250*0Sstevel@tonic-gate    my $p = shift;
251*0Sstevel@tonic-gate    my $dosnames=(defined(&Dos::UseLFN) && Dos::UseLFN()==0);
252*0Sstevel@tonic-gate    my $read = maniread() || {};
253*0Sstevel@tonic-gate    my $found = manifind($p);
254*0Sstevel@tonic-gate
255*0Sstevel@tonic-gate    my(@missfile) = ();
256*0Sstevel@tonic-gate    foreach my $file (_sort keys %$read){
257*0Sstevel@tonic-gate        warn "Debug: manicheck checking from $MANIFEST $file\n" if $Debug;
258*0Sstevel@tonic-gate        if ($dosnames){
259*0Sstevel@tonic-gate            $file = lc $file;
260*0Sstevel@tonic-gate            $file =~ s=(\.(\w|-)+)=substr ($1,0,4)=ge;
261*0Sstevel@tonic-gate            $file =~ s=((\w|-)+)=substr ($1,0,8)=ge;
262*0Sstevel@tonic-gate        }
263*0Sstevel@tonic-gate        unless ( exists $found->{$file} ) {
264*0Sstevel@tonic-gate            warn "No such file: $file\n" unless $Quiet;
265*0Sstevel@tonic-gate            push @missfile, $file;
266*0Sstevel@tonic-gate        }
267*0Sstevel@tonic-gate    }
268*0Sstevel@tonic-gate
269*0Sstevel@tonic-gate    return @missfile;
270*0Sstevel@tonic-gate}
271*0Sstevel@tonic-gate
272*0Sstevel@tonic-gate
273*0Sstevel@tonic-gatesub _check_manifest {
274*0Sstevel@tonic-gate    my($p) = @_;
275*0Sstevel@tonic-gate    my $read = maniread() || {};
276*0Sstevel@tonic-gate    my $found = manifind($p);
277*0Sstevel@tonic-gate    my $skip  = _maniskip();
278*0Sstevel@tonic-gate
279*0Sstevel@tonic-gate    my @missentry = ();
280*0Sstevel@tonic-gate    foreach my $file (_sort keys %$found){
281*0Sstevel@tonic-gate        next if $skip->($file);
282*0Sstevel@tonic-gate        warn "Debug: manicheck checking from disk $file\n" if $Debug;
283*0Sstevel@tonic-gate        unless ( exists $read->{$file} ) {
284*0Sstevel@tonic-gate            my $canon = $Is_MacOS ? "\t" . _unmacify($file) : '';
285*0Sstevel@tonic-gate            warn "Not in $MANIFEST: $file$canon\n" unless $Quiet;
286*0Sstevel@tonic-gate            push @missentry, $file;
287*0Sstevel@tonic-gate        }
288*0Sstevel@tonic-gate    }
289*0Sstevel@tonic-gate
290*0Sstevel@tonic-gate    return @missentry;
291*0Sstevel@tonic-gate}
292*0Sstevel@tonic-gate
293*0Sstevel@tonic-gate
294*0Sstevel@tonic-gate=item maniread
295*0Sstevel@tonic-gate
296*0Sstevel@tonic-gate    my $manifest = maniread();
297*0Sstevel@tonic-gate    my $manifest = maniread($manifest_file);
298*0Sstevel@tonic-gate
299*0Sstevel@tonic-gatereads a named C<MANIFEST> file (defaults to C<MANIFEST> in the current
300*0Sstevel@tonic-gatedirectory) and returns a HASH reference with files being the keys and
301*0Sstevel@tonic-gatecomments being the values of the HASH.  Blank lines and lines which
302*0Sstevel@tonic-gatestart with C<#> in the C<MANIFEST> file are discarded.
303*0Sstevel@tonic-gate
304*0Sstevel@tonic-gate=cut
305*0Sstevel@tonic-gate
306*0Sstevel@tonic-gatesub maniread {
307*0Sstevel@tonic-gate    my ($mfile) = @_;
308*0Sstevel@tonic-gate    $mfile ||= $MANIFEST;
309*0Sstevel@tonic-gate    my $read = {};
310*0Sstevel@tonic-gate    local *M;
311*0Sstevel@tonic-gate    unless (open M, $mfile){
312*0Sstevel@tonic-gate        warn "$mfile: $!";
313*0Sstevel@tonic-gate        return $read;
314*0Sstevel@tonic-gate    }
315*0Sstevel@tonic-gate    local $_;
316*0Sstevel@tonic-gate    while (<M>){
317*0Sstevel@tonic-gate        chomp;
318*0Sstevel@tonic-gate        next if /^\s*#/;
319*0Sstevel@tonic-gate
320*0Sstevel@tonic-gate        my($file, $comment) = /^(\S+)\s*(.*)/;
321*0Sstevel@tonic-gate        next unless $file;
322*0Sstevel@tonic-gate
323*0Sstevel@tonic-gate        if ($Is_MacOS) {
324*0Sstevel@tonic-gate            $file = _macify($file);
325*0Sstevel@tonic-gate            $file =~ s/\\([0-3][0-7][0-7])/sprintf("%c", oct($1))/ge;
326*0Sstevel@tonic-gate        }
327*0Sstevel@tonic-gate        elsif ($Is_VMS) {
328*0Sstevel@tonic-gate            require File::Basename;
329*0Sstevel@tonic-gate            my($base,$dir) = File::Basename::fileparse($file);
330*0Sstevel@tonic-gate            # Resolve illegal file specifications in the same way as tar
331*0Sstevel@tonic-gate            $dir =~ tr/./_/;
332*0Sstevel@tonic-gate            my(@pieces) = split(/\./,$base);
333*0Sstevel@tonic-gate            if (@pieces > 2) { $base = shift(@pieces) . '.' . join('_',@pieces); }
334*0Sstevel@tonic-gate            my $okfile = "$dir$base";
335*0Sstevel@tonic-gate            warn "Debug: Illegal name $file changed to $okfile\n" if $Debug;
336*0Sstevel@tonic-gate            $file = $okfile;
337*0Sstevel@tonic-gate            $file = lc($file) unless $file =~ /^MANIFEST(\.SKIP)?$/;
338*0Sstevel@tonic-gate        }
339*0Sstevel@tonic-gate
340*0Sstevel@tonic-gate        $read->{$file} = $comment;
341*0Sstevel@tonic-gate    }
342*0Sstevel@tonic-gate    close M;
343*0Sstevel@tonic-gate    $read;
344*0Sstevel@tonic-gate}
345*0Sstevel@tonic-gate
346*0Sstevel@tonic-gate# returns an anonymous sub that decides if an argument matches
347*0Sstevel@tonic-gatesub _maniskip {
348*0Sstevel@tonic-gate    my @skip ;
349*0Sstevel@tonic-gate    my $mfile = "$MANIFEST.SKIP";
350*0Sstevel@tonic-gate    local(*M,$_);
351*0Sstevel@tonic-gate    open M, $mfile or open M, $DEFAULT_MSKIP or return sub {0};
352*0Sstevel@tonic-gate    while (<M>){
353*0Sstevel@tonic-gate	chomp;
354*0Sstevel@tonic-gate	next if /^#/;
355*0Sstevel@tonic-gate	next if /^\s*$/;
356*0Sstevel@tonic-gate	push @skip, _macify($_);
357*0Sstevel@tonic-gate    }
358*0Sstevel@tonic-gate    close M;
359*0Sstevel@tonic-gate    my $opts = $Is_VMS ? '(?i)' : '';
360*0Sstevel@tonic-gate
361*0Sstevel@tonic-gate    # Make sure each entry is isolated in its own parentheses, in case
362*0Sstevel@tonic-gate    # any of them contain alternations
363*0Sstevel@tonic-gate    my $regex = join '|', map "(?:$_)", @skip;
364*0Sstevel@tonic-gate
365*0Sstevel@tonic-gate    return sub { $_[0] =~ qr{$opts$regex} };
366*0Sstevel@tonic-gate}
367*0Sstevel@tonic-gate
368*0Sstevel@tonic-gate=item manicopy
369*0Sstevel@tonic-gate
370*0Sstevel@tonic-gate    manicopy($src, $dest_dir);
371*0Sstevel@tonic-gate    manicopy($src, $dest_dir, $how);
372*0Sstevel@tonic-gate
373*0Sstevel@tonic-gatecopies the files that are the keys in the HASH I<%$src> to the
374*0Sstevel@tonic-gate$dest_dir. The HASH reference $read is typically returned by the
375*0Sstevel@tonic-gatemaniread() function. This function is useful for producing a directory
376*0Sstevel@tonic-gatetree identical to the intended distribution tree. The third parameter
377*0Sstevel@tonic-gate$how can be used to specify a different methods of "copying". Valid
378*0Sstevel@tonic-gatevalues are C<cp>, which actually copies the files, C<ln> which creates
379*0Sstevel@tonic-gatehard links, and C<best> which mostly links the files but copies any
380*0Sstevel@tonic-gatesymbolic link to make a tree without any symbolic link. Best is the
381*0Sstevel@tonic-gatedefault.
382*0Sstevel@tonic-gate
383*0Sstevel@tonic-gate=cut
384*0Sstevel@tonic-gate
385*0Sstevel@tonic-gatesub manicopy {
386*0Sstevel@tonic-gate    my($read,$target,$how)=@_;
387*0Sstevel@tonic-gate    croak "manicopy() called without target argument" unless defined $target;
388*0Sstevel@tonic-gate    $how ||= 'cp';
389*0Sstevel@tonic-gate    require File::Path;
390*0Sstevel@tonic-gate    require File::Basename;
391*0Sstevel@tonic-gate
392*0Sstevel@tonic-gate    $target = VMS::Filespec::unixify($target) if $Is_VMS;
393*0Sstevel@tonic-gate    File::Path::mkpath([ $target ],! $Quiet,$Is_VMS ? undef : 0755);
394*0Sstevel@tonic-gate    foreach my $file (keys %$read){
395*0Sstevel@tonic-gate    	if ($Is_MacOS) {
396*0Sstevel@tonic-gate	    if ($file =~ m!:!) {
397*0Sstevel@tonic-gate	   	my $dir = _maccat($target, $file);
398*0Sstevel@tonic-gate		$dir =~ s/[^:]+$//;
399*0Sstevel@tonic-gate	    	File::Path::mkpath($dir,1,0755);
400*0Sstevel@tonic-gate	    }
401*0Sstevel@tonic-gate	    cp_if_diff($file, _maccat($target, $file), $how);
402*0Sstevel@tonic-gate	} else {
403*0Sstevel@tonic-gate	    $file = VMS::Filespec::unixify($file) if $Is_VMS;
404*0Sstevel@tonic-gate	    if ($file =~ m!/!) { # Ilya, that hurts, I fear, or maybe not?
405*0Sstevel@tonic-gate		my $dir = File::Basename::dirname($file);
406*0Sstevel@tonic-gate		$dir = VMS::Filespec::unixify($dir) if $Is_VMS;
407*0Sstevel@tonic-gate		File::Path::mkpath(["$target/$dir"],! $Quiet,$Is_VMS ? undef : 0755);
408*0Sstevel@tonic-gate	    }
409*0Sstevel@tonic-gate	    cp_if_diff($file, "$target/$file", $how);
410*0Sstevel@tonic-gate	}
411*0Sstevel@tonic-gate    }
412*0Sstevel@tonic-gate}
413*0Sstevel@tonic-gate
414*0Sstevel@tonic-gatesub cp_if_diff {
415*0Sstevel@tonic-gate    my($from, $to, $how)=@_;
416*0Sstevel@tonic-gate    -f $from or carp "$0: $from not found";
417*0Sstevel@tonic-gate    my($diff) = 0;
418*0Sstevel@tonic-gate    local(*F,*T);
419*0Sstevel@tonic-gate    open(F,"< $from\0") or die "Can't read $from: $!\n";
420*0Sstevel@tonic-gate    if (open(T,"< $to\0")) {
421*0Sstevel@tonic-gate        local $_;
422*0Sstevel@tonic-gate	while (<F>) { $diff++,last if $_ ne <T>; }
423*0Sstevel@tonic-gate	$diff++ unless eof(T);
424*0Sstevel@tonic-gate	close T;
425*0Sstevel@tonic-gate    }
426*0Sstevel@tonic-gate    else { $diff++; }
427*0Sstevel@tonic-gate    close F;
428*0Sstevel@tonic-gate    if ($diff) {
429*0Sstevel@tonic-gate	if (-e $to) {
430*0Sstevel@tonic-gate	    unlink($to) or confess "unlink $to: $!";
431*0Sstevel@tonic-gate	}
432*0Sstevel@tonic-gate      STRICT_SWITCH: {
433*0Sstevel@tonic-gate	    best($from,$to), last STRICT_SWITCH if $how eq 'best';
434*0Sstevel@tonic-gate	    cp($from,$to), last STRICT_SWITCH if $how eq 'cp';
435*0Sstevel@tonic-gate	    ln($from,$to), last STRICT_SWITCH if $how eq 'ln';
436*0Sstevel@tonic-gate	    croak("ExtUtils::Manifest::cp_if_diff " .
437*0Sstevel@tonic-gate		  "called with illegal how argument [$how]. " .
438*0Sstevel@tonic-gate		  "Legal values are 'best', 'cp', and 'ln'.");
439*0Sstevel@tonic-gate	}
440*0Sstevel@tonic-gate    }
441*0Sstevel@tonic-gate}
442*0Sstevel@tonic-gate
443*0Sstevel@tonic-gatesub cp {
444*0Sstevel@tonic-gate    my ($srcFile, $dstFile) = @_;
445*0Sstevel@tonic-gate    my ($perm,$access,$mod) = (stat $srcFile)[2,8,9];
446*0Sstevel@tonic-gate    copy($srcFile,$dstFile);
447*0Sstevel@tonic-gate    utime $access, $mod + ($Is_VMS ? 1 : 0), $dstFile;
448*0Sstevel@tonic-gate    # chmod a+rX-w,go-w
449*0Sstevel@tonic-gate    chmod(  0444 | ( $perm & 0111 ? 0111 : 0 ),  $dstFile )
450*0Sstevel@tonic-gate      unless ($^O eq 'MacOS');
451*0Sstevel@tonic-gate}
452*0Sstevel@tonic-gate
453*0Sstevel@tonic-gatesub ln {
454*0Sstevel@tonic-gate    my ($srcFile, $dstFile) = @_;
455*0Sstevel@tonic-gate    return &cp if $Is_VMS or ($^O eq 'MSWin32' and Win32::IsWin95());
456*0Sstevel@tonic-gate    link($srcFile, $dstFile);
457*0Sstevel@tonic-gate
458*0Sstevel@tonic-gate    # chmod a+r,go-w+X (except "X" only applies to u=x)
459*0Sstevel@tonic-gate    local($_) = $dstFile;
460*0Sstevel@tonic-gate    my $mode= 0444 | (stat)[2] & 0700;
461*0Sstevel@tonic-gate    if (! chmod(  $mode | ( $mode & 0100 ? 0111 : 0 ),  $_  )) {
462*0Sstevel@tonic-gate        unlink $dstFile;
463*0Sstevel@tonic-gate        return;
464*0Sstevel@tonic-gate    }
465*0Sstevel@tonic-gate    1;
466*0Sstevel@tonic-gate}
467*0Sstevel@tonic-gate
468*0Sstevel@tonic-gateunless (defined $Config{d_link}) {
469*0Sstevel@tonic-gate    # Really cool fix from Ilya :)
470*0Sstevel@tonic-gate    local $SIG{__WARN__} = sub {
471*0Sstevel@tonic-gate        warn @_ unless $_[0] =~ /^Subroutine .* redefined/;
472*0Sstevel@tonic-gate    };
473*0Sstevel@tonic-gate    *ln = \&cp;
474*0Sstevel@tonic-gate}
475*0Sstevel@tonic-gate
476*0Sstevel@tonic-gate
477*0Sstevel@tonic-gate
478*0Sstevel@tonic-gate
479*0Sstevel@tonic-gatesub best {
480*0Sstevel@tonic-gate    my ($srcFile, $dstFile) = @_;
481*0Sstevel@tonic-gate    if (-l $srcFile) {
482*0Sstevel@tonic-gate	cp($srcFile, $dstFile);
483*0Sstevel@tonic-gate    } else {
484*0Sstevel@tonic-gate	ln($srcFile, $dstFile) or cp($srcFile, $dstFile);
485*0Sstevel@tonic-gate    }
486*0Sstevel@tonic-gate}
487*0Sstevel@tonic-gate
488*0Sstevel@tonic-gatesub _macify {
489*0Sstevel@tonic-gate    my($file) = @_;
490*0Sstevel@tonic-gate
491*0Sstevel@tonic-gate    return $file unless $Is_MacOS;
492*0Sstevel@tonic-gate
493*0Sstevel@tonic-gate    $file =~ s|^\./||;
494*0Sstevel@tonic-gate    if ($file =~ m|/|) {
495*0Sstevel@tonic-gate	$file =~ s|/+|:|g;
496*0Sstevel@tonic-gate	$file = ":$file";
497*0Sstevel@tonic-gate    }
498*0Sstevel@tonic-gate
499*0Sstevel@tonic-gate    $file;
500*0Sstevel@tonic-gate}
501*0Sstevel@tonic-gate
502*0Sstevel@tonic-gatesub _maccat {
503*0Sstevel@tonic-gate    my($f1, $f2) = @_;
504*0Sstevel@tonic-gate
505*0Sstevel@tonic-gate    return "$f1/$f2" unless $Is_MacOS;
506*0Sstevel@tonic-gate
507*0Sstevel@tonic-gate    $f1 .= ":$f2";
508*0Sstevel@tonic-gate    $f1 =~ s/([^:]:):/$1/g;
509*0Sstevel@tonic-gate    return $f1;
510*0Sstevel@tonic-gate}
511*0Sstevel@tonic-gate
512*0Sstevel@tonic-gatesub _unmacify {
513*0Sstevel@tonic-gate    my($file) = @_;
514*0Sstevel@tonic-gate
515*0Sstevel@tonic-gate    return $file unless $Is_MacOS;
516*0Sstevel@tonic-gate
517*0Sstevel@tonic-gate    $file =~ s|^:||;
518*0Sstevel@tonic-gate    $file =~ s|([/ \n])|sprintf("\\%03o", unpack("c", $1))|ge;
519*0Sstevel@tonic-gate    $file =~ y|:|/|;
520*0Sstevel@tonic-gate
521*0Sstevel@tonic-gate    $file;
522*0Sstevel@tonic-gate}
523*0Sstevel@tonic-gate
524*0Sstevel@tonic-gate
525*0Sstevel@tonic-gate=item maniadd
526*0Sstevel@tonic-gate
527*0Sstevel@tonic-gate  maniadd({ $file => $comment, ...});
528*0Sstevel@tonic-gate
529*0Sstevel@tonic-gateAdds an entry to an existing F<MANIFEST> unless its already there.
530*0Sstevel@tonic-gate
531*0Sstevel@tonic-gate$file will be normalized (ie. Unixified).  B<UNIMPLEMENTED>
532*0Sstevel@tonic-gate
533*0Sstevel@tonic-gate=cut
534*0Sstevel@tonic-gate
535*0Sstevel@tonic-gatesub maniadd {
536*0Sstevel@tonic-gate    my($additions) = shift;
537*0Sstevel@tonic-gate
538*0Sstevel@tonic-gate    _normalize($additions);
539*0Sstevel@tonic-gate    _fix_manifest($MANIFEST);
540*0Sstevel@tonic-gate
541*0Sstevel@tonic-gate    my $manifest = maniread();
542*0Sstevel@tonic-gate    my @needed = grep { !exists $manifest->{$_} } keys %$additions;
543*0Sstevel@tonic-gate    return 1 unless @needed;
544*0Sstevel@tonic-gate
545*0Sstevel@tonic-gate    open(MANIFEST, ">>$MANIFEST") or
546*0Sstevel@tonic-gate      die "maniadd() could not open $MANIFEST: $!";
547*0Sstevel@tonic-gate
548*0Sstevel@tonic-gate    foreach my $file (_sort @needed) {
549*0Sstevel@tonic-gate        my $comment = $additions->{$file} || '';
550*0Sstevel@tonic-gate        printf MANIFEST "%-40s %s\n", $file, $comment;
551*0Sstevel@tonic-gate    }
552*0Sstevel@tonic-gate    close MANIFEST or die "Error closing $MANIFEST: $!";
553*0Sstevel@tonic-gate
554*0Sstevel@tonic-gate    return 1;
555*0Sstevel@tonic-gate}
556*0Sstevel@tonic-gate
557*0Sstevel@tonic-gate
558*0Sstevel@tonic-gate# Sometimes MANIFESTs are missing a trailing newline.  Fix this.
559*0Sstevel@tonic-gatesub _fix_manifest {
560*0Sstevel@tonic-gate    my $manifest_file = shift;
561*0Sstevel@tonic-gate
562*0Sstevel@tonic-gate    open MANIFEST, $MANIFEST or die "Could not open $MANIFEST: $!";
563*0Sstevel@tonic-gate
564*0Sstevel@tonic-gate    # Yes, we should be using seek(), but I'd like to avoid loading POSIX
565*0Sstevel@tonic-gate    # to get SEEK_*
566*0Sstevel@tonic-gate    my @manifest = <MANIFEST>;
567*0Sstevel@tonic-gate    close MANIFEST;
568*0Sstevel@tonic-gate
569*0Sstevel@tonic-gate    unless( $manifest[-1] =~ /\n\z/ ) {
570*0Sstevel@tonic-gate        open MANIFEST, ">>$MANIFEST" or die "Could not open $MANIFEST: $!";
571*0Sstevel@tonic-gate        print MANIFEST "\n";
572*0Sstevel@tonic-gate        close MANIFEST;
573*0Sstevel@tonic-gate    }
574*0Sstevel@tonic-gate}
575*0Sstevel@tonic-gate
576*0Sstevel@tonic-gate
577*0Sstevel@tonic-gate# UNIMPLEMENTED
578*0Sstevel@tonic-gatesub _normalize {
579*0Sstevel@tonic-gate    return;
580*0Sstevel@tonic-gate}
581*0Sstevel@tonic-gate
582*0Sstevel@tonic-gate
583*0Sstevel@tonic-gate=back
584*0Sstevel@tonic-gate
585*0Sstevel@tonic-gate=head2 MANIFEST
586*0Sstevel@tonic-gate
587*0Sstevel@tonic-gateAnything between white space and an end of line within a C<MANIFEST>
588*0Sstevel@tonic-gatefile is considered to be a comment.  Filenames and comments are
589*0Sstevel@tonic-gateseparated by one or more TAB characters in the output.
590*0Sstevel@tonic-gate
591*0Sstevel@tonic-gate
592*0Sstevel@tonic-gate=head2 MANIFEST.SKIP
593*0Sstevel@tonic-gate
594*0Sstevel@tonic-gateThe file MANIFEST.SKIP may contain regular expressions of files that
595*0Sstevel@tonic-gateshould be ignored by mkmanifest() and filecheck(). The regular
596*0Sstevel@tonic-gateexpressions should appear one on each line. Blank lines and lines
597*0Sstevel@tonic-gatewhich start with C<#> are skipped.  Use C<\#> if you need a regular
598*0Sstevel@tonic-gateexpression to start with a sharp character. A typical example:
599*0Sstevel@tonic-gate
600*0Sstevel@tonic-gate    # Version control files and dirs.
601*0Sstevel@tonic-gate    \bRCS\b
602*0Sstevel@tonic-gate    \bCVS\b
603*0Sstevel@tonic-gate    ,v$
604*0Sstevel@tonic-gate    \B\.svn\b
605*0Sstevel@tonic-gate
606*0Sstevel@tonic-gate    # Makemaker generated files and dirs.
607*0Sstevel@tonic-gate    ^MANIFEST\.
608*0Sstevel@tonic-gate    ^Makefile$
609*0Sstevel@tonic-gate    ^blib/
610*0Sstevel@tonic-gate    ^MakeMaker-\d
611*0Sstevel@tonic-gate
612*0Sstevel@tonic-gate    # Temp, old and emacs backup files.
613*0Sstevel@tonic-gate    ~$
614*0Sstevel@tonic-gate    \.old$
615*0Sstevel@tonic-gate    ^#.*#$
616*0Sstevel@tonic-gate    ^\.#
617*0Sstevel@tonic-gate
618*0Sstevel@tonic-gateIf no MANIFEST.SKIP file is found, a default set of skips will be
619*0Sstevel@tonic-gateused, similar to the example above.  If you want nothing skipped,
620*0Sstevel@tonic-gatesimply make an empty MANIFEST.SKIP file.
621*0Sstevel@tonic-gate
622*0Sstevel@tonic-gate
623*0Sstevel@tonic-gate=head2 EXPORT_OK
624*0Sstevel@tonic-gate
625*0Sstevel@tonic-gateC<&mkmanifest>, C<&manicheck>, C<&filecheck>, C<&fullcheck>,
626*0Sstevel@tonic-gateC<&maniread>, and C<&manicopy> are exportable.
627*0Sstevel@tonic-gate
628*0Sstevel@tonic-gate=head2 GLOBAL VARIABLES
629*0Sstevel@tonic-gate
630*0Sstevel@tonic-gateC<$ExtUtils::Manifest::MANIFEST> defaults to C<MANIFEST>. Changing it
631*0Sstevel@tonic-gateresults in both a different C<MANIFEST> and a different
632*0Sstevel@tonic-gateC<MANIFEST.SKIP> file. This is useful if you want to maintain
633*0Sstevel@tonic-gatedifferent distributions for different audiences (say a user version
634*0Sstevel@tonic-gateand a developer version including RCS).
635*0Sstevel@tonic-gate
636*0Sstevel@tonic-gateC<$ExtUtils::Manifest::Quiet> defaults to 0. If set to a true value,
637*0Sstevel@tonic-gateall functions act silently.
638*0Sstevel@tonic-gate
639*0Sstevel@tonic-gateC<$ExtUtils::Manifest::Debug> defaults to 0.  If set to a true value,
640*0Sstevel@tonic-gateor if PERL_MM_MANIFEST_DEBUG is true, debugging output will be
641*0Sstevel@tonic-gateproduced.
642*0Sstevel@tonic-gate
643*0Sstevel@tonic-gate=head1 DIAGNOSTICS
644*0Sstevel@tonic-gate
645*0Sstevel@tonic-gateAll diagnostic output is sent to C<STDERR>.
646*0Sstevel@tonic-gate
647*0Sstevel@tonic-gate=over 4
648*0Sstevel@tonic-gate
649*0Sstevel@tonic-gate=item C<Not in MANIFEST:> I<file>
650*0Sstevel@tonic-gate
651*0Sstevel@tonic-gateis reported if a file is found which is not in C<MANIFEST>.
652*0Sstevel@tonic-gate
653*0Sstevel@tonic-gate=item C<Skipping> I<file>
654*0Sstevel@tonic-gate
655*0Sstevel@tonic-gateis reported if a file is skipped due to an entry in C<MANIFEST.SKIP>.
656*0Sstevel@tonic-gate
657*0Sstevel@tonic-gate=item C<No such file:> I<file>
658*0Sstevel@tonic-gate
659*0Sstevel@tonic-gateis reported if a file mentioned in a C<MANIFEST> file does not
660*0Sstevel@tonic-gateexist.
661*0Sstevel@tonic-gate
662*0Sstevel@tonic-gate=item C<MANIFEST:> I<$!>
663*0Sstevel@tonic-gate
664*0Sstevel@tonic-gateis reported if C<MANIFEST> could not be opened.
665*0Sstevel@tonic-gate
666*0Sstevel@tonic-gate=item C<Added to MANIFEST:> I<file>
667*0Sstevel@tonic-gate
668*0Sstevel@tonic-gateis reported by mkmanifest() if $Verbose is set and a file is added
669*0Sstevel@tonic-gateto MANIFEST. $Verbose is set to 1 by default.
670*0Sstevel@tonic-gate
671*0Sstevel@tonic-gate=back
672*0Sstevel@tonic-gate
673*0Sstevel@tonic-gate=head1 ENVIRONMENT
674*0Sstevel@tonic-gate
675*0Sstevel@tonic-gate=over 4
676*0Sstevel@tonic-gate
677*0Sstevel@tonic-gate=item B<PERL_MM_MANIFEST_DEBUG>
678*0Sstevel@tonic-gate
679*0Sstevel@tonic-gateTurns on debugging
680*0Sstevel@tonic-gate
681*0Sstevel@tonic-gate=back
682*0Sstevel@tonic-gate
683*0Sstevel@tonic-gate=head1 SEE ALSO
684*0Sstevel@tonic-gate
685*0Sstevel@tonic-gateL<ExtUtils::MakeMaker> which has handy targets for most of the functionality.
686*0Sstevel@tonic-gate
687*0Sstevel@tonic-gate=head1 AUTHOR
688*0Sstevel@tonic-gate
689*0Sstevel@tonic-gateAndreas Koenig <F<andreas.koenig@anima.de>>
690*0Sstevel@tonic-gate
691*0Sstevel@tonic-gate=cut
692*0Sstevel@tonic-gate
693*0Sstevel@tonic-gate1;
694