#! /usr/bin/perl # touch tar archive mtime timestamp # Usage: $0 [-z] [-n] files ... # -z also check gzip archive time # -n don't actually touch, show what would be done use strict; use warnings; use Getopt::Std; getopts('zn', \my %opt); use List::Util q(max); use IO::Uncompress::AnyUncompress; use constant HDR_UNPACK => ' a100 a8 a8 a8 a12 a12 a8 a1 a100 a6 a2 a32 a32 a8 a8 a155 a12'; use constant HDR_FIELDS => qw(name mode uid gid size mtime chksum type linkname magic version uname gname devmajor devminor prefix _pad); use constant HDR_OCTALS => qw(mode uid gid size mtime chksum devmajor devminor); sub tar_header { my ($hdr, $r) = @_; @$r{HDR_FIELDS,} = map /([^\0]*)/, unpack(HDR_UNPACK, $hdr); #return if $$r{magic} !~ /^ustar/; # some tar-s use weird magic return if $$r{_pad} ne '' || grep /[^0-7 ]/, @$r{HDR_OCTALS,}; $_ = oct for @$r{HDR_OCTALS,}; # fix octal fields substr($hdr, 148, 8) = ' 'x8; $$r{chksum} == unpack('%C*', $hdr) || $$r{chksum} == unpack('%c*', $hdr) } sub tar_time { my ($fnam, $skip, $mtime, $ztime, $fh, $buf) = (shift, 0, 0); $fh = new IO::Uncompress::AnyUncompress($fnam) or return; $ztime = $opt{z} && ($fh->getHeaderInfo||{})->{Time} || 0; while (read($fh, $buf, 0x200) == 0x200) { next if $skip-- > 0; next if !tar_header($buf, \my %h); $mtime = $h{mtime} if $mtime < $h{mtime}; next if $h{type} && $h{type} ne "L"; $skip = ($h{size} + 0x1ff) >> 9; $skip = 0 if $skip && seek($fh, $skip<<9, 1); } return { 'gzip' => $ztime, 'tar' => $mtime }; } foreach (@ARGV) { my ($r, $t); -f && ($r = tar_time($_)) && ($t = max values %$r) && ($t != (stat)[9]) && ($opt{n} || utime($t, $t, $_)) && printf "%-60s %s (%s)\n", $_, scalar localtime($t), $t == $r->{tar} ? q(tar time) : q(zip time); }