This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Added note about directories not being created with File::Copy::copy
[perl5.git] / lib / File / Copy.pm
CommitLineData
f716a1dd
PP
1# File/Copy.pm. Written in 1994 by Aaron Sherman <ajs@ajs.com>. This
2# source code has been placed in the public domain by the author.
3# Please be kind and preserve the documentation.
4#
71be2cbc
PP
5# Additions copyright 1996 by Charles Bailey. Permission is granted
6# to distribute the revised code under the same terms as Perl itself.
f716a1dd
PP
7
8package File::Copy;
9
3b825e41 10use 5.006;
71be2cbc 11use strict;
32450e7f 12use warnings; no warnings 'newline';
6c254d95 13use File::Spec;
96a91e01 14use Config;
e63b3379 15# During perl build, we need File::Copy but Scalar::Util might not be built yet
e55c0a82 16# And then we need these games to avoid loading overload, as that will
17# confuse miniperl during the bootstrap of perl.
18my $Scalar_Util_loaded = eval q{ require Scalar::Util; require overload; 1 };
17f410f9
GS
19our(@ISA, @EXPORT, @EXPORT_OK, $VERSION, $Too_Big, $Syscopy_is_copy);
20sub copy;
21sub syscopy;
22sub cp;
23sub mv;
71be2cbc 24
86e39d7d 25$VERSION = '2.27';
f716a1dd 26
71be2cbc
PP
27require Exporter;
28@ISA = qw(Exporter);
29@EXPORT = qw(copy move);
30@EXPORT_OK = qw(cp mv);
f716a1dd 31
441496b2 32$Too_Big = 1024 * 1024 * 2;
f716a1dd 33
8878f897
T
34sub croak {
35 require Carp;
36 goto &Carp::croak;
37}
38
754f2cd0
MS
39sub carp {
40 require Carp;
41 goto &Carp::carp;
42}
43
fc06fdeb
JM
44# Look up the feature settings on VMS using VMS::Feature when available.
45
46my $use_vms_feature = 0;
47BEGIN {
48 if ($^O eq 'VMS') {
37930f0f 49 if (eval { local $SIG{__DIE__}; require VMS::Feature; }) {
fc06fdeb
JM
50 $use_vms_feature = 1;
51 }
52 }
53}
54
55# Need to look up the UNIX report mode. This may become a dynamic mode
56# in the future.
57sub _vms_unix_rpt {
58 my $unix_rpt;
59 if ($use_vms_feature) {
60 $unix_rpt = VMS::Feature::current("filename_unix_report");
61 } else {
62 my $env_unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
5da2a9e6 63 $unix_rpt = $env_unix_rpt =~ /^[ET1]/i;
fc06fdeb
JM
64 }
65 return $unix_rpt;
66}
67
68# Need to look up the EFS character set mode. This may become a dynamic
69# mode in the future.
70sub _vms_efs {
71 my $efs;
72 if ($use_vms_feature) {
73 $efs = VMS::Feature::current("efs_charset");
74 } else {
75 my $env_efs = $ENV{'DECC$EFS_CHARSET'} || '';
5da2a9e6 76 $efs = $env_efs =~ /^[ET1]/i;
fc06fdeb
JM
77 }
78 return $efs;
79}
80
81
6c254d95 82sub _catname {
71be2cbc
PP
83 my($from, $to) = @_;
84 if (not defined &basename) {
85 require File::Basename;
86 import File::Basename 'basename';
87 }
6c254d95 88
6c254d95 89 return File::Spec->catfile($to, basename($from));
f716a1dd
PP
90}
91
236a0738 92# _eq($from, $to) tells whether $from and $to are identical
236a0738 93sub _eq {
e55c0a82 94 my ($from, $to) = map {
95 $Scalar_Util_loaded && Scalar::Util::blessed($_)
96 && overload::Method($_, q{""})
97 ? "$_"
98 : $_
99 } (@_);
100 return '' if ( (ref $from) xor (ref $to) );
101 return $from == $to if ref $from;
102 return $from eq $to;
236a0738
AF
103}
104
f716a1dd 105sub copy {
71be2cbc 106 croak("Usage: copy(FROM, TO [, BUFFERSIZE]) ")
f716a1dd
PP
107 unless(@_ == 2 || @_ == 3);
108
109 my $from = shift;
110 my $to = shift;
71be2cbc 111
671637fe
NC
112 my $size;
113 if (@_) {
114 $size = shift(@_) + 0;
115 croak("Bad buffer size for copy: $size\n") unless ($size > 0);
116 }
117
71be2cbc
PP
118 my $from_a_handle = (ref($from)
119 ? (ref($from) eq 'GLOB'
d704f39a
G
120 || UNIVERSAL::isa($from, 'GLOB')
121 || UNIVERSAL::isa($from, 'IO::Handle'))
71be2cbc
PP
122 : (ref(\$from) eq 'GLOB'));
123 my $to_a_handle = (ref($to)
124 ? (ref($to) eq 'GLOB'
d704f39a
G
125 || UNIVERSAL::isa($to, 'GLOB')
126 || UNIVERSAL::isa($to, 'IO::Handle'))
71be2cbc
PP
127 : (ref(\$to) eq 'GLOB'));
128
236a0738 129 if (_eq($from, $to)) { # works for references, too
754f2cd0 130 carp("'$from' and '$to' are identical (not copied)");
39b80fd9 131 return 0;
96a91e01 132 }
133
43ddfa56
TC
134 if (!$from_a_handle && !$to_a_handle && -d $to && ! -d $from) {
135 $to = _catname($from, $to);
136 }
137
ac7b122d 138 if ((($Config{d_symlink} && $Config{d_readlink}) || $Config{d_link}) &&
4c38808d 139 !($^O eq 'MSWin32' || $^O eq 'os2')) {
ac7b122d
SR
140 my @fs = stat($from);
141 if (@fs) {
96a91e01 142 my @ts = stat($to);
16f708c9 143 if (@ts && $fs[0] == $ts[0] && $fs[1] == $ts[1] && !-p $from) {
754f2cd0
MS
144 carp("'$from' and '$to' are identical (not copied)");
145 return 0;
96a91e01 146 }
147 }
148 }
a0084943
SH
149 elsif (_eq($from, $to)) {
150 carp("'$from' and '$to' are identical (not copied)");
151 return 0;
152 }
96a91e01 153
1a04d035 154 if (defined &syscopy && !$Syscopy_is_copy
e6434134 155 && !$to_a_handle
1d84e8df 156 && !($from_a_handle && $^O eq 'os2' ) # OS/2 cannot handle handles
7509b657 157 && !($from_a_handle && $^O eq 'MSWin32')
2986a63f 158 && !($from_a_handle && $^O eq 'NetWare')
1a04d035 159 )
71be2cbc 160 {
4c38808d
JM
161 my $copy_to = $to;
162
163 if ($^O eq 'VMS' && -e $from) {
164
165 if (! -d $to && ! -d $from) {
166
fc06fdeb
JM
167 my $vms_efs = _vms_efs();
168 my $unix_rpt = _vms_unix_rpt();
169 my $unix_mode = 0;
170 my $from_unix = 0;
171 $from_unix = 1 if ($from =~ /^\.\.?$/);
172 my $from_vms = 0;
173 $from_vms = 1 if ($from =~ m#[\[<\]]#);
174
175 # Need to know if we are in Unix mode.
176 if ($from_vms == $from_unix) {
177 $unix_mode = $unix_rpt;
178 } else {
179 $unix_mode = $from_unix;
180 }
181
4c38808d
JM
182 # VMS has sticky defaults on extensions, which means that
183 # if there is a null extension on the destination file, it
184 # will inherit the extension of the source file
185 # So add a '.' for a null extension.
186
fc06fdeb
JM
187 # In unix_rpt mode, the trailing dot should not be added.
188
189 if ($vms_efs) {
190 $copy_to = $to;
191 } else {
192 $copy_to = VMS::Filespec::vmsify($to);
193 }
4c38808d 194 my ($vol, $dirs, $file) = File::Spec->splitpath($copy_to);
fc06fdeb
JM
195 $file = $file . '.'
196 unless (($file =~ /(?<!\^)\./) || $unix_rpt);
4c38808d
JM
197 $copy_to = File::Spec->catpath($vol, $dirs, $file);
198
199 # Get rid of the old versions to be like UNIX
200 1 while unlink $copy_to;
201 }
202 }
203
079cb8cc 204 return syscopy($from, $copy_to) || 0;
71be2cbc
PP
205 }
206
207 my $closefrom = 0;
208 my $closeto = 0;
671637fe 209 my ($status, $r, $buf);
48a5c399 210 local($\) = '';
f716a1dd 211
23ba2776 212 my $from_h;
71be2cbc 213 if ($from_a_handle) {
23ba2776 214 $from_h = $from;
f716a1dd 215 } else {
cfa308ca 216 open $from_h, "<", $from or goto fail_open1;
23ba2776 217 binmode $from_h or die "($!,$^E)";
e63b3379 218 $closefrom = 1;
1a04d035
A
219 }
220
671637fe
NC
221 # Seems most logical to do this here, in case future changes would want to
222 # make this croak for some reason.
223 unless (defined $size) {
224 $size = tied(*$from_h) ? 0 : -s $from_h || 0;
225 $size = 1024 if ($size < 512);
226 $size = $Too_Big if ($size > $Too_Big);
227 }
228
23ba2776 229 my $to_h;
71be2cbc 230 if ($to_a_handle) {
23ba2776 231 $to_h = $to;
1a04d035 232 } else {
fff5c6e2 233 $to_h = \do { local *FH }; # XXX is this line obsolete?
e63b3379 234 open $to_h, ">", $to or goto fail_open2;
91ca337e 235 binmode $to_h or die "($!,$^E)";
71be2cbc 236 $closeto = 1;
1a04d035 237 }
f716a1dd 238
71be2cbc
PP
239 $! = 0;
240 for (;;) {
241 my ($r, $w, $t);
23ba2776 242 defined($r = sysread($from_h, $buf, $size))
71be2cbc
PP
243 or goto fail_inner;
244 last unless $r;
245 for ($w = 0; $w < $r; $w += $t) {
23ba2776 246 $t = syswrite($to_h, $buf, $r - $w, $w)
71be2cbc 247 or goto fail_inner;
f716a1dd
PP
248 }
249 }
71be2cbc 250
23ba2776
WD
251 close($to_h) || goto fail_open2 if $closeto;
252 close($from_h) || goto fail_open1 if $closefrom;
71be2cbc 253
48a5c399 254 # Use this idiom to avoid uninitialized value warning.
f716a1dd 255 return 1;
1a04d035 256
f716a1dd
PP
257 # All of these contortions try to preserve error messages...
258 fail_inner:
259 if ($closeto) {
260 $status = $!;
261 $! = 0;
23ba2776 262 close $to_h;
f716a1dd
PP
263 $! = $status unless $!;
264 }
265 fail_open2:
266 if ($closefrom) {
267 $status = $!;
268 $! = 0;
23ba2776 269 close $from_h;
f716a1dd
PP
270 $! = $status unless $!;
271 }
272 fail_open1:
f716a1dd
PP
273 return 0;
274}
9b957b78 275
e63b3379 276sub cp {
71be2cbc 277 my($from,$to) = @_;
e63b3379
CB
278 my(@fromstat) = stat $from;
279 my(@tostat) = stat $to;
280 my $perm;
281
282 return 0 unless copy(@_) and @fromstat;
283
284 if (@tostat) {
285 $perm = $tostat[2];
286 } else {
287 $perm = $fromstat[2] & ~(umask || 0);
288 @tostat = stat $to;
289 }
290 # Might be more robust to look for S_I* in Fcntl, but we're
291 # trying to avoid dependence on any XS-containing modules,
292 # since File::Copy is used during the Perl build.
293 $perm &= 07777;
294 if ($perm & 06000) {
295 croak("Unable to check setuid/setgid permissions for $to: $!")
296 unless @tostat;
297
298 if ($perm & 04000 and # setuid
299 $fromstat[4] != $tostat[4]) { # owner must match
300 $perm &= ~06000;
301 }
302
ed62bc33 303 if ($perm & 02000 && $> != 0) { # if not root, setgid
e63b3379
CB
304 my $ok = $fromstat[5] == $tostat[5]; # group must match
305 if ($ok) { # and we must be in group
ed62bc33 306 $ok = grep { $_ == $fromstat[5] } split /\s+/, $)
e63b3379
CB
307 }
308 $perm &= ~06000 unless $ok;
309 }
310 }
311 return 0 unless @tostat;
312 return 1 if $perm == ($tostat[2] & 07777);
313 return eval { chmod $perm, $to; } ? 1 : 0;
314}
315
316sub _move {
317 croak("Usage: move(FROM, TO) ") unless @_ == 3;
318
319 my($from,$to,$fallback) = @_;
754f2cd0 320
fa76202e 321 my($fromsz,$tosz1,$tomt1,$tosz2,$tomt2,$sts,$ossts);
441496b2 322
71be2cbc
PP
323 if (-d $to && ! -d $from) {
324 $to = _catname($from, $to);
325 }
326
327 ($tosz1,$tomt1) = (stat($to))[7,9];
328 $fromsz = -s $from;
e6434134
IZ
329 if ($^O eq 'os2' and defined $tosz1 and defined $fromsz) {
330 # will not rename with overwrite
331 unlink $to;
332 }
4c38808d
JM
333
334 my $rename_to = $to;
335 if (-$^O eq 'VMS' && -e $from) {
336
337 if (! -d $to && ! -d $from) {
fc06fdeb
JM
338
339 my $vms_efs = _vms_efs();
340 my $unix_rpt = _vms_unix_rpt();
341 my $unix_mode = 0;
342 my $from_unix = 0;
343 $from_unix = 1 if ($from =~ /^\.\.?$/);
344 my $from_vms = 0;
345 $from_vms = 1 if ($from =~ m#[\[<\]]#);
346
347 # Need to know if we are in Unix mode.
348 if ($from_vms == $from_unix) {
349 $unix_mode = $unix_rpt;
350 } else {
351 $unix_mode = $from_unix;
352 }
353
4c38808d
JM
354 # VMS has sticky defaults on extensions, which means that
355 # if there is a null extension on the destination file, it
356 # will inherit the extension of the source file
357 # So add a '.' for a null extension.
358
fc06fdeb
JM
359 # In unix_rpt mode, the trailing dot should not be added.
360
361 if ($vms_efs) {
362 $rename_to = $to;
363 } else {
364 $rename_to = VMS::Filespec::vmsify($to);
365 }
4c38808d 366 my ($vol, $dirs, $file) = File::Spec->splitpath($rename_to);
fc06fdeb
JM
367 $file = $file . '.'
368 unless (($file =~ /(?<!\^)\./) || $unix_rpt);
4c38808d
JM
369 $rename_to = File::Spec->catpath($vol, $dirs, $file);
370
371 # Get rid of the old versions to be like UNIX
372 1 while unlink $rename_to;
373 }
374 }
375
376 return 1 if rename $from, $rename_to;
71be2cbc 377
71be2cbc
PP
378 # Did rename return an error even though it succeeded, because $to
379 # is on a remote NFS file system, and NFS lost the server's ack?
380 return 1 if defined($fromsz) && !-e $from && # $from disappeared
381 (($tosz2,$tomt2) = (stat($to))[7,9]) && # $to's there
c9fbd0c8
JM
382 ((!defined $tosz1) || # not before or
383 ($tosz1 != $tosz2 or $tomt1 != $tomt2)) && # was changed
71be2cbc 384 $tosz2 == $fromsz; # it's all there
1a04d035 385
71be2cbc 386 ($tosz1,$tomt1) = (stat($to))[7,9]; # just in case rename did something
762548ba
MS
387
388 {
389 local $@;
390 eval {
754f2cd0 391 local $SIG{__DIE__};
e63b3379 392 $fallback->($from,$to) or die;
762548ba
MS
393 my($atime, $mtime) = (stat($from))[8,9];
394 utime($atime, $mtime, $to);
395 unlink($from) or die;
396 };
397 return 1 unless $@;
398 }
fa76202e 399 ($sts,$ossts) = ($! + 0, $^E + 0);
1a04d035 400
71be2cbc
PP
401 ($tosz2,$tomt2) = ((stat($to))[7,9],0,0) if defined $tomt1;
402 unlink($to) if !defined($tomt1) or $tomt1 != $tomt2 or $tosz1 != $tosz2;
403 ($!,$^E) = ($sts,$ossts);
404 return 0;
441496b2 405}
9b957b78 406
e63b3379
CB
407sub move { _move(@_,\&copy); }
408sub mv { _move(@_,\&cp); }
71be2cbc 409
9b957b78 410# &syscopy is an XSUB under OS/2
1d84e8df
JH
411unless (defined &syscopy) {
412 if ($^O eq 'VMS') {
413 *syscopy = \&rmscopy;
cf2f24a4
JD
414 } elsif ($^O eq 'MSWin32' && defined &DynaLoader::boot_DynaLoader) {
415 # Win32::CopyFile() fill only work if we can load Win32.xs
7509b657
GS
416 *syscopy = sub {
417 return 0 unless @_ == 2;
418 return Win32::CopyFile(@_, 1);
419 };
1d84e8df 420 } else {
1a04d035 421 $Syscopy_is_copy = 1;
1d84e8df
JH
422 *syscopy = \&copy;
423 }
424}
f716a1dd
PP
425
4261;
427
428__END__
a5f75d66 429
f716a1dd
PP
430=head1 NAME
431
432File::Copy - Copy files or filehandles
433
a5f75d66 434=head1 SYNOPSIS
f716a1dd 435
5ce10329 436 use File::Copy;
f716a1dd 437
86e39d7d 438 copy("sourcefile","destinationfile") or die "Copy failed: $!";
5ce10329 439 copy("Copy.pm",\*STDOUT);
86e39d7d 440 move("/dev1/sourcefile","/dev2/destinationfile");
f716a1dd 441
78e38bb6 442 use File::Copy "cp";
f716a1dd 443
23f3aea0 444 $n = FileHandle->new("/a/file","r");
c6dfe06b 445 cp($n,"x");
f716a1dd
PP
446
447=head1 DESCRIPTION
448
441496b2
CB
449The File::Copy module provides two basic functions, C<copy> and
450C<move>, which are useful for getting the contents of a file from
451one place to another.
452
453=over 4
454
0cdecedb
GS
455=item copy
456X<copy> X<cp>
441496b2
CB
457
458The C<copy> function takes two
f716a1dd
PP
459parameters: a file to copy from and a file to copy to. Either
460argument may be a string, a FileHandle reference or a FileHandle
461glob. Obviously, if the first argument is a filehandle of some
462sort, it will be read from, and if it is a file I<name> it will
463be opened for reading. Likewise, the second argument will be
ab3dadc3
NG
464written to. If the second argument does not exist but the parent
465directory does exist, then it will be created. Trying to copy
466a file into a non-existent directory is an error.
467Trying to copy a file on top of itself is also an error.
71be2cbc 468
1f3ebc3b
A
469If the destination (second argument) already exists and is a directory,
470and the source (first argument) is not a filehandle, then the source
471file will be copied into the directory specified by the destination,
472using the same base name as the source file. It's a failure to have a
473filehandle as the source when the destination is a directory.
474
71be2cbc 475B<Note that passing in
9b957b78
PP
476files as handles instead of names may lead to loss of information
477on some operating systems; it is recommended that you use file
e6434134 478names whenever possible.> Files are opened in binary mode where
8dcee03e 479applicable. To get a consistent behaviour when copying from a
e6434134 480filehandle to a file, use C<binmode> on the filehandle.
f716a1dd
PP
481
482An optional third parameter can be used to specify the buffer
483size used for copying. This is the number of bytes from the
3a964d77 484first file, that will be held in memory at any given time, before
f716a1dd 485being written to the second file. The default buffer size depends
338de517 486upon the file, but will generally be the whole file (up to 2MB), or
f716a1dd
PP
4871k for filehandles that do not reference files (eg. sockets).
488
e63b3379
CB
489You may use the syntax C<use File::Copy "cp"> to get at the C<cp>
490alias for this function. The syntax is I<exactly> the same. The
402c5d17 491behavior is nearly the same as well: as of version 2.15, C<cp> will
e63b3379
CB
492preserve the source file's permission bits like the shell utility
493C<cp(1)> would do, while C<copy> uses the default permissions for the
494target file (which may depend on the process' C<umask>, file
495ownership, inherited ACLs, etc.). If an error occurs in setting
496permissions, C<cp> will return 0, regardless of whether the file was
497successfully copied.
9c76cba2 498
0cdecedb
GS
499=item move
500X<move> X<mv> X<rename>
441496b2
CB
501
502The C<move> function also takes two parameters: the current name
71be2cbc
PP
503and the intended name of the file to be moved. If the destination
504already exists and is a directory, and the source is not a
505directory, then the source file will be renamed into the directory
506specified by the destination.
507
508If possible, move() will simply rename the file. Otherwise, it copies
509the file to the new location and deletes the original. If an error occurs
510during this copy-and-delete process, you may be left with a (possibly partial)
441496b2
CB
511copy of the file under the destination name.
512
e63b3379 513You may use the C<mv> alias for this function in the same way that
402c5d17 514you may use the C<cp> alias for C<copy>.
441496b2 515
0cdecedb
GS
516=item syscopy
517X<syscopy>
441496b2 518
9b957b78
PP
519File::Copy also provides the C<syscopy> routine, which copies the
520file specified in the first parameter to the file specified in the
521second parameter, preserving OS-specific attributes and file
522structure. For Unix systems, this is equivalent to the simple
f1442e8b
SB
523C<copy> routine, which doesn't preserve OS-specific attributes. For
524VMS systems, this calls the C<rmscopy> routine (see below). For OS/2
525systems, this calls the C<syscopy> XSUB directly. For Win32 systems,
526this calls C<Win32::CopyFile>.
9b957b78 527
338de517 528B<Special behaviour if C<syscopy> is defined (OS/2, VMS and Win32)>:
9b957b78 529
71be2cbc
PP
530If both arguments to C<copy> are not file handles,
531then C<copy> will perform a "system copy" of
9b957b78
PP
532the input file to a new output file, in order to preserve file
533attributes, indexed file structure, I<etc.> The buffer size
71be2cbc
PP
534parameter is ignored. If either argument to C<copy> is a
535handle to an opened file, then data is copied using Perl
9b957b78
PP
536operators, and no effort is made to preserve file attributes
537or record structure.
538
55497cff
PP
539The system copy routine may also be called directly under VMS and OS/2
540as C<File::Copy::syscopy> (or under VMS as C<File::Copy::rmscopy>, which
71be2cbc 541is the routine that does the actual work for syscopy).
9b957b78
PP
542
543=item rmscopy($from,$to[,$date_flag])
0cdecedb 544X<rmscopy>
9b957b78 545
71be2cbc
PP
546The first and second arguments may be strings, typeglobs, typeglob
547references, or objects inheriting from IO::Handle;
548they are used in all cases to obtain the
9b957b78
PP
549I<filespec> of the input and output files, respectively. The
550name and type of the input file are used as defaults for the
551output file, if necessary.
552
553A new version of the output file is always created, which
554inherits the structure and RMS attributes of the input file,
555except for owner and protections (and possibly timestamps;
556see below). All data from the input file is copied to the
557output file; if either of the first two parameters to C<rmscopy>
558is a file handle, its position is unchanged. (Note that this
559means a file handle pointing to the output file will be
560associated with an old version of that file after C<rmscopy>
561returns, not the newly created version.)
562
563The third parameter is an integer flag, which tells C<rmscopy>
1fef88e7
JM
564how to handle timestamps. If it is E<lt> 0, none of the input file's
565timestamps are propagated to the output file. If it is E<gt> 0, then
9b957b78
PP
566it is interpreted as a bitmask: if bit 0 (the LSB) is set, then
567timestamps other than the revision date are propagated; if bit 1
568is set, the revision date is propagated. If the third parameter
569to C<rmscopy> is 0, then it behaves much like the DCL COPY command:
570if the name or type of the output file was explicitly specified,
571then no timestamps are propagated, but if they were taken implicitly
572from the input filespec, then all timestamps other than the
573revision date are propagated. If this parameter is not supplied,
574it defaults to 0.
575
576Like C<copy>, C<rmscopy> returns 1 on success. If an error occurs,
577it sets C<$!>, deletes the output file, and returns 0.
578
55497cff
PP
579=back
580
f716a1dd
PP
581=head1 RETURN
582
441496b2
CB
583All functions return 1 on success, 0 on failure.
584$! will be set if an error was encountered.
f716a1dd
PP
585
586=head1 AUTHOR
587
441496b2 588File::Copy was written by Aaron Sherman I<E<lt>ajs@ajs.comE<gt>> in 1995,
bd3fa61c 589and updated by Charles Bailey I<E<lt>bailey@newman.upenn.eduE<gt>> in 1996.
f716a1dd
PP
590
591=cut
441496b2 592