123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- #! @PERL@ -w
- #
- # Written by Adam Byrtek <alpha@debian.org>, 2002
- #
- # extfs to handle patches in unified diff format
- use bytes;
- use strict;
- use POSIX;
- # 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(0);
- }
- # print message on stderr and exit
- sub error
- {
- print STDERR $_[0];
- exit 1;
- }
- # list files affected by patch
- sub list
- {
- my ($archive)=@_;
- 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 mens comments
- $state=1; $f="";
- while (<I>) {
- if (/^-{3} /) {
- # parse diff header
- if ($state==1) {
- $npos=tell(I)-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>)=~/^\-{3} .*\n\+{3} .*\n@@ .* @@\n$/);
- ($fsrc)=/^-{3} ([^\s]+).*\n.*\n.*\n$/;
- ($fdst)=/^.*\n\+{3} ([^\s]+).*\n.*\n$/;
- ($time)=/^.*\n\+{3} [^\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 && !/^([+\- ]|@@)/) {
- # start of comments, end of diff contents
- $npos=tell(I)-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=tell(I);
- 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 (/^-{3} /) {
- # parse diff header
- last if ($state==1 && $found);
- $state=1;
- error "Can't parse unified diff header"
- unless ((($_.=<I>).=<I>)=~/^\-{3} .*\n\+{3} .*\n@@ .* @@\n$/);
- ($fsrc)=/^-{3} ([^\s]+).*\n.*\n.*\n$/;
- ($fdst)=/^.*\n\+{3} ([^\s]+).*\n.*\n$/;
- $found=1 if (($fsrc eq $file) || ($fdst eq $file));
- } elsif ($state==1 && !/^([+\- ]|@@)/) {
- # 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 $ARGV[1] |";
- } elsif (/gzip/) {
- open I, "$gzip -dc $ARGV[1] |";
- } 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;
|