📄 tar.pm
字号:
=cutsub clear { my $self = shift or return; $self->_data( [] ); $self->_file( '' ); return 1;}=head2 $tar->write ( [$file, $compressed, $prefix] )Write the in-memory archive to disk. The first argument can eitherbe the name of a file or a reference to an already open filehandle (aGLOB reference). If the second argument is true, the module will useIO::Zlib to write the file in a compressed format. If IO::Zlib isnot available, the C<write> method will fail and return.Note that when you pass in a filehandle, the compression argumentis ignored, as all files are printed verbatim to your filehandle.If you wish to enable compression with filehandles, use anC<IO::Zlib> filehandle instead.Specific levels of compression can be chosen by passing the values 2through 9 as the second parameter.The third argument is an optional prefix. All files will be tuckedaway in the directory you specify as prefix. So if you have files'a' and 'b' in your archive, and you specify 'foo' as prefix, theywill be written to the archive as 'foo/a' and 'foo/b'.If no arguments are given, C<write> returns the entire formattedarchive as a string, which could be useful if you'd like to stuff thearchive into a socket or a pipe to gzip or something.=cutsub write { my $self = shift; my $file = shift; $file = '' unless defined $file; my $gzip = shift || 0; my $ext_prefix = shift; $ext_prefix = '' unless defined $ext_prefix; my $dummy = ''; ### only need a handle if we have a file to print to ### my $handle = length($file) ? ( $self->_get_handle($file, $gzip, WRITE_ONLY->($gzip) ) or return ) : $HAS_PERLIO ? do { open my $h, '>', \$dummy; $h } : $HAS_IO_STRING ? IO::String->new : __PACKAGE__->no_string_support(); for my $entry ( @{$self->_data} ) { ### entries to be written to the tarfile ### my @write_me; ### only now will we change the object to reflect the current state ### of the name and prefix fields -- this needs to be limited to ### write() only! my $clone = $entry->clone; ### so, if you don't want use to use the prefix, we'll stuff ### everything in the name field instead if( $DO_NOT_USE_PREFIX ) { ### you might have an extended prefix, if so, set it in the clone ### XXX is ::Unix right? $clone->name( length $ext_prefix ? File::Spec::Unix->catdir( $ext_prefix, $clone->full_path) : $clone->full_path ); $clone->prefix( '' ); ### otherwise, we'll have to set it properly -- prefix part in the ### prefix and name part in the name field. } else { ### split them here, not before! my ($prefix,$name) = $clone->_prefix_and_file( $clone->full_path ); ### you might have an extended prefix, if so, set it in the clone ### XXX is ::Unix right? $prefix = File::Spec::Unix->catdir( $ext_prefix, $prefix ) if length $ext_prefix; $clone->prefix( $prefix ); $clone->name( $name ); } ### names are too long, and will get truncated if we don't add a ### '@LongLink' file... my $make_longlink = ( length($clone->name) > NAME_LENGTH or length($clone->prefix) > PREFIX_LENGTH ) || 0; ### perhaps we need to make a longlink file? if( $make_longlink ) { my $longlink = Archive::Tar::File->new( data => LONGLINK_NAME, $clone->full_path, { type => LONGLINK } ); unless( $longlink ) { $self->_error( qq[Could not create 'LongLink' entry for ] . qq[oversize file '] . $clone->full_path ."'" ); return; }; push @write_me, $longlink; } push @write_me, $clone; ### write the one, optionally 2 a::t::file objects to the handle for my $clone (@write_me) { ### if the file is a symlink, there are 2 options: ### either we leave the symlink intact, but then we don't write any ### data OR we follow the symlink, which means we actually make a ### copy. if we do the latter, we have to change the TYPE of the ### clone to 'FILE' my $link_ok = $clone->is_symlink && $Archive::Tar::FOLLOW_SYMLINK; my $data_ok = !$clone->is_symlink && $clone->has_content; ### downgrade to a 'normal' file if it's a symlink we're going to ### treat as a regular file $clone->_downgrade_to_plainfile if $link_ok; ### get the header for this block my $header = $self->_format_tar_entry( $clone ); unless( $header ) { $self->_error(q[Could not format header for: ] . $clone->full_path ); return; } unless( print $handle $header ) { $self->_error(q[Could not write header for: ] . $clone->full_path); return; } if( $link_ok or $data_ok ) { unless( print $handle $clone->data ) { $self->_error(q[Could not write data for: ] . $clone->full_path); return; } ### pad the end of the clone if required ### print $handle TAR_PAD->( $clone->size ) if $clone->size % BLOCK } } ### done writing these entries } ### write the end markers ### print $handle TAR_END x 2 or return $self->_error( qq[Could not write tar end markers] ); ### did you want it written to a file, or returned as a string? ### my $rv = length($file) ? 1 : $HAS_PERLIO ? $dummy : do { seek $handle, 0, 0; local $/; <$handle> }; ### make sure to close the handle; close $handle; return $rv;}sub _format_tar_entry { my $self = shift; my $entry = shift or return; my $ext_prefix = shift; $ext_prefix = '' unless defined $ext_prefix; my $no_prefix = shift || 0; my $file = $entry->name; my $prefix = $entry->prefix; $prefix = '' unless defined $prefix; ### remove the prefix from the file name ### not sure if this is still neeeded --kane ### no it's not -- Archive::Tar::File->_new_from_file will take care of ### this for us. Even worse, this would break if we tried to add a file ### like x/x. #if( length $prefix ) { # $file =~ s/^$match//; #} $prefix = File::Spec::Unix->catdir($ext_prefix, $prefix) if length $ext_prefix; ### not sure why this is... ### my $l = PREFIX_LENGTH; # is ambiguous otherwise... substr ($prefix, 0, -$l) = "" if length $prefix >= PREFIX_LENGTH; my $f1 = "%06o"; my $f2 = "%11o"; ### this might be optimizable with a 'changed' flag in the file objects ### my $tar = pack ( PACK, $file, (map { sprintf( $f1, $entry->$_() ) } qw[mode uid gid]), (map { sprintf( $f2, $entry->$_() ) } qw[size mtime]), "", # checksum field - space padded a bit down (map { $entry->$_() } qw[type linkname magic]), $entry->version || TAR_VERSION, (map { $entry->$_() } qw[uname gname]), (map { sprintf( $f1, $entry->$_() ) } qw[devmajor devminor]), ($no_prefix ? '' : $prefix) ); ### add the checksum ### substr($tar,148,7) = sprintf("%6o\0", unpack("%16C*",$tar)); return $tar;}=head2 $tar->add_files( @filenamelist )Takes a list of filenames and adds them to the in-memory archive.The path to the file is automatically converted to a Unix likeequivalent for use in the archive, and, if on MacOS, the file'smodification time is converted from the MacOS epoch to the Unix epoch.So tar archives created on MacOS with B<Archive::Tar> can be readboth with I<tar> on Unix and applications like I<suntar> orI<Stuffit Expander> on MacOS.Be aware that the file's type/creator and resource fork will be lost,which is usually what you want in cross-platform archives.Returns a list of C<Archive::Tar::File> objects that were just added.=cutsub add_files { my $self = shift; my @files = @_ or return; my @rv; for my $file ( @files ) { unless( -e $file || -l $file ) { $self->_error( qq[No such file: '$file'] ); next; } my $obj = Archive::Tar::File->new( file => $file ); unless( $obj ) { $self->_error( qq[Unable to add file: '$file'] ); next; } push @rv, $obj; } push @{$self->{_data}}, @rv; return @rv;}=head2 $tar->add_data ( $filename, $data, [$opthashref] )Takes a filename, a scalar full of data and optionally a reference toa hash with specific options.Will add a file to the in-memory archive, with name C<$filename> andcontent C<$data>. Specific properties can be set using C<$opthashref>.The following list of properties is supported: name, size, mtime(last modified date), mode, uid, gid, linkname, uname, gname,devmajor, devminor, prefix, type. (On MacOS, the file's path andmodification times are converted to Unix equivalents.)Valid values for the file type are the following constants defined inArchive::Tar::Constants:=over 4=item FILERegular file.=item HARDLINK=item SYMLINKHard and symbolic ("soft") links; linkname should specify target.=item CHARDEV=item BLOCKDEVCharacter and block devices. devmajor and devminor should specify the majorand minor device numbers.=item DIRDirectory.=item FIFOFIFO (named pipe).=item SOCKETSocket.=backReturns the C<Archive::Tar::File> object that was just added, orC<undef> on failure.=cutsub add_data { my $self = shift; my ($file, $data, $opt) = @_; my $obj = Archive::Tar::File->new( data => $file, $data, $opt ); unless( $obj ) { $self->_error( qq[Unable to add file: '$file'] ); return; } push @{$self->{_data}}, $obj; return $obj;}=head2 $tar->error( [$BOOL] )Returns the current errorstring (usually, the last error reported).If a true value was specified, it will give the C<Carp::longmess>equivalent of the error, in effect giving you a stacktrace.For backwards compatibility, this error is also available asC<$Archive::Tar::error> although it is much recommended you use themethod call instead.=cut{ $error = ''; my $longmess; sub _error { my $self = shift; my $msg = $error = shift; $longmess = Carp::longmess($error); ### set Archive::Tar::WARN to 0 to disable printing ### of errors if( $WARN ) { carp $DEBUG ? $longmess : $msg; } return; } sub error { my $self = shift; return shift() ? $longmess : $error; }}=head2 $tar->setcwd( $cwd );C<Archive::Tar> needs to know the current directory, and it will runC<Cwd::cwd()> I<every> time it extracts a I<relative> entry from the tarfile and saves it in the file system. (As of version 1.30, however,C<Archive::Tar> will use the speed optimization described below automatically, so it's only relevant if you're using C<extract_file()>).Since C<Archive::Tar> doesn't change the current directory internallywhile it is extracting the items in a tarball, all calls to C<Cwd::cwd()>can be avoided if we can guarantee that the current directory doesn'tget changed externally.To use this performance boost, set the current directory via use Cwd; $tar->setcwd( cwd() );once before calling a function like C<extract_file> andC<Archive::Tar> will use the current directory setting from then onand won't call C<Cwd::cwd()> internally. To switch back to the default behaviour, use $tar->setcwd( undef );and C<Archive::Tar> will call C<Cwd::cwd()> internally again.If you're using C<Archive::Tar>'s C<exract()> method, C<setcwd()> willbe called for you.=cut sub setcwd { my $self = shift; my $cwd = shift; $self->{cwd} = $cwd;}=head2 $bool = $tar->has_io_stringReturns true if we currently have C<IO::String> support loaded.Either C<IO::String> or C<perlio> support is needed to support writing stringified archives. Currently, C<perlio> is the preferred method, ifavailable.See the C<GLOBAL VARIABLES> section to see how to change this preference.=cutsub has_io_string { return $HAS_IO_STRING; }=head2 $bool = $tar->has_perlioReturns true if we currently have C<perlio> support loaded.This requires C<perl-5.8> or higher, compiled with C<perlio> Either C<IO::String> or C<perlio> support is needed to support writing stringified archives. Currently, C<perlio> is the preferred method, ifavailable.See the C<GLOBAL VARIABLES> section to see how to change this preference.=cutsub has_perlio { return $HAS_PERLIO; }=head1 Class Methods=head2 Archive::Tar->create_archive($file, $compression, @filelist)Creates a tar file from the list of files provided. The firstargument can either be the name of the tar file to create or areference to an open file handle (e.g. a GLOB reference).The second argument specifies the level of compression to be used, ifany. Compression of tar files requires the installation of theIO::Zlib module. Specific levels of compression may berequested by passing a value between 2 and 9 as the second argument.Any other value evaluating as true will result in the defaultcompression level being used.Note that when you pass in a filehandle, the compression argumentis ignored, as all files are printed verbatim to your filehandle.If you wish to enable compression with filehandles, use an
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -