mc/vfs/extfs/patchfs.in

218 lines
5.1 KiB
Plaintext

#! @PERL@ -w
#
# Written by Adam Byrtek <alpha@debian.org>, 2002
#
# extfs to handle patches in unified diff format
use strict;
# standard binaries
my $bzip = "bzip2";
my $gzip = "gzip";
my $file = "file";
# date parsing requires Date::Parse from TimeDate module
my $parsedates = eval "require Date::Parse";
# output unix date in a mc-readable format
sub timef
{
my @time=localtime($_[0]);
return sprintf "%02d-%02d-%02d %02d:%02d", $time[4]+1, $time[3],
$time[5]+1900, $time[2], $time[1];
}
# parse given string as a date and return unix time
sub datetime
{
# in case of problems fall back to 0 in unix time
# note: str2time interprets some wrong values (eg. " ") as 'today'
if ($parsedates && defined (my $t=str2time($_[0]))) {
return timef($t);
}
return timef(time);
}
# print message on stderr and exit
sub error
{
print STDERR $_[0], "\n";
exit 1;
}
# list files affected by patch
sub list
{
my ($archive)=(quotemeta $_[0]);
my ($state,$pos,$npos,$time);
my ($f,$fsrc,$fdst,$prefix);
# use uid and gid from file
my ($uid,$gid)=(`ls -l $archive`=~/^[^\s]+\s+[^\s]+\s+([^\s]+)\s+([^\s]+)/);
import Date::Parse if ($parsedates);
# state==1 means diff contents, state==0 means comments
$state = 1; $f = '';
my $cpos = 0;
while (<I>) {
$cpos += length;
if (/^--- /) {
# parse diff header
if ($state==1) {
$npos = $cpos - length;
printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $npos-$pos, datetime($time), $prefix, $f
if $f;
$pos=$npos;
}
$state=1;
error "Can't parse unified diff header"
unless ((($_.=<I>).=<I>)=~/^\--- .*\n\+\+\+ .*\n@@ .* @@.*\n$/);
($fsrc)=/^--- ([^\s]+).*\n.*\n.*\n$/;
($fdst)=/^.*\n\+\+\+ ([^\s]+).*\n.*\n$/;
($time)=/^.*\n\+\+\+ [^\s]+\s+([^\t\n]+).*\n.*\n$/;
# select filename, conform with (diff.info)Multiple patches
$prefix="";
if ($fsrc eq "/dev/null") {
$f=$fdst; $prefix="PATCH-CREATE/";
} elsif ($fdst eq "/dev/null") {
$f=$fsrc; $prefix="PATCH-REMOVE/";
} elsif (($fdst eq "/dev/null") && ($fsrc eq "/dev/null")) {
error "Malformed diff";
} elsif (!$fdst && !$fsrc) {
error "Index: not yet implemented";
} else {
# fewest path name components
if ($fdst=~s|/|/|g < $fsrc=~s|/|/|g) {
$f=$fdst;
} elsif ($fdst=~s|/|/|g > $fsrc=~s|/|/|g) {
$f=$fsrc;
} else {
# shorter base name
if (($fdst=~m|^.*/([^/]+)$|,length $1) < ($fsrc=~m|^.*/([^/]+)$|,length $1)) {
$f=$fdst;
} elsif (($fdst=~m|^.*/([^/]+)$|,length $1) > ($fsrc=~m|^.*/([^/]+)$|,length $1)) {
$f=$fsrc;
} else {
# shortest names
if (length $fdst < length $fsrc) {
$f=$fdst;
} else {
$f=$fsrc;
}
}
}
}
$f=$f.".diff";
} elsif ($state==1 && !/^([+\- \n]|@@)/) {
# start of comments, end of diff contents
$npos = $cpos - length;
printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $npos-$pos, datetime($time), $prefix, $f
if $f;
$pos=$npos;
$state=0;
}
}
$npos = $cpos;
printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $npos-$pos, datetime($time), $prefix, $f
if $f;
}
sub copyout
{
my ($file,$out)=@_;
my ($fsrc,$fdst,$found,$state,$buf);
$file=~s/^(PATCH-(CREATE|REMOVE)\/)?(.*)\.diff$/$3/;
# state==1 means diff contents, state==0 mens comments
$state=1; $found=0; $buf="";
while (<I>) {
if (/^--- /) {
# parse diff header
last if ($state==1 && $found);
$state=1;
error "Can't parse unified diff header"
unless ((($_.=<I>).=<I>)=~/^\--- .*\n\+\+\+ .*\n@@ .* @@.*\n$/);
($fsrc)=/^--- ([^\s]+).*\n.*\n.*\n$/;
($fdst)=/^.*\n\+\+\+ ([^\s]+).*\n.*\n$/;
$found=1 if (($fsrc eq $file) || ($fdst eq $file));
} elsif ($state==1 && !/^([+\- \n]|@@)/) {
# start of comments, end of diff contents
last if ($found);
$state=0;
$buf="";
}
$buf.=$_ if ($found || $state==0)
}
if ($found) {
open O, "> $out";
print O $buf;
close O;
}
}
sub copyin
{
# append diff to archive
my ($archive,$name,$f)=(quotemeta $_[0],$_[1],quotemeta $_[2]);
my ($cmd);
error "File must have .diff or .patch extension"
unless $name=~/\.(diff|patch)(\.(bz|bz2|gz|z|Z))?$/;
$_=`$file $f`;
if (/bzip/) {
$cmd="$bzip -dc $f";
} elsif (/gzip/) {
$cmd="$gzip -dc $f";
} else {
$cmd="cat $f";
}
$_=`$file $archive`;
if (/bzip/) {
system "$cmd | $bzip -c >> $archive";
} elsif (/gzip/) {
system "$cmd | $gzip -c >> $archive";
} else {
system "$cmd >> $archive";
}
}
sub openread
{
# open (compressed) archive for reading
my ($archive) = (quotemeta $_[0]);
$_=`$file $archive`;
if (/bzip/) {
open I, "$bzip -dc $archive |";
} elsif (/gzip/) {
open I, "$gzip -dc $archive |";
} else {
open I, "< $ARGV[1]";
}
}
if ($ARGV[0] eq "list") {
openread $ARGV[1];
list $ARGV[1];
exit 0;
} if ($ARGV[0] eq "copyout") {
openread $ARGV[1];
copyout ($ARGV[2], $ARGV[3]);
exit 0;
} if ($ARGV[0] eq "copyin") {
copyin ($ARGV[1], $ARGV[2], $ARGV[3]);
exit 0;
}
exit 1;