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