patchfs.in 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. #! @PERL@ -w
  2. #
  3. # Written by Adam Byrtek <alpha@debian.org>, 2002
  4. #
  5. # extfs to handle patches in unified diff format
  6. use bytes;
  7. use strict;
  8. use POSIX;
  9. # standard binaries
  10. my $bzip = "bzip2";
  11. my $gzip = "gzip";
  12. my $file = "file";
  13. # date parsing requires Date::Parse from TimeDate module
  14. my $parsedates = eval "require Date::Parse";
  15. # output unix date in a mc-readable format
  16. sub timef
  17. {
  18. my @time=localtime($_[0]);
  19. return sprintf "%02d-%02d-%02d %02d:%02d", $time[4]+1, $time[3],
  20. $time[5]+1900, $time[2], $time[1];
  21. }
  22. # parse given string as a date and return unix time
  23. sub datetime
  24. {
  25. # in case of problems fall back to 0 in unix time
  26. # note: str2time interprets some wrong values (eg. " ") as 'today'
  27. if ($parsedates && defined (my $t=str2time($_[0]))) {
  28. return timef($t)
  29. }
  30. return timef(0);
  31. }
  32. # print message on stderr and exit
  33. sub error
  34. {
  35. print STDERR $_[0];
  36. exit 1;
  37. }
  38. # list files affected by patch
  39. sub list
  40. {
  41. my ($archive)=@_;
  42. my ($state,$pos,$npos,$time);
  43. my ($f,$fsrc,$fdst,$prefix);
  44. # use uid and gid from file
  45. my ($uid,$gid)=(`ls -l $archive`=~/^[^\s]+\s+[^\s]+\s+([^\s]+)\s+([^\s]+)/);
  46. import Date::Parse if ($parsedates);
  47. # state==1 means diff contents, state==0 mens comments
  48. $state=1; $f="";
  49. while (<I>) {
  50. if (/^-{3} /) {
  51. # parse diff header
  52. if ($state==1) {
  53. $npos=tell(I)-length;
  54. printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $npos-$pos, datetime($time), $prefix, $f
  55. if $f;
  56. $pos=$npos;
  57. }
  58. $state=1;
  59. error "Can't parse unified diff header"
  60. unless ((($_.=<I>).=<I>)=~/^\-{3} .*\n\+{3} .*\n@@ .* @@\n$/);
  61. ($fsrc)=/^-{3} ([^\s]+).*\n.*\n.*\n$/;
  62. ($fdst)=/^.*\n\+{3} ([^\s]+).*\n.*\n$/;
  63. ($time)=/^.*\n\+{3} [^\s]+\s+([^\t\n]+).*\n.*\n$/;
  64. # select filename, conform with (diff.info)Multiple patches
  65. $prefix="";
  66. if ($fsrc eq "/dev/null") {
  67. $f=$fdst; $prefix="PATCH-CREATE/";
  68. } elsif ($fdst eq "/dev/null") {
  69. $f=$fsrc; $prefix="PATCH-REMOVE/";
  70. } elsif (($fdst eq "/dev/null") && ($fsrc eq "/dev/null")) {
  71. error "Malformed diff";
  72. } elsif (!$fdst && !$fsrc) {
  73. error "Index: not yet implemented";
  74. } else {
  75. # fewest path name components
  76. if ($fdst=~s|/|/|g < $fsrc=~s|/|/|g) {
  77. $f=$fdst;
  78. } elsif ($fdst=~s|/|/|g > $fsrc=~s|/|/|g) {
  79. $f=$fsrc;
  80. } else {
  81. # shorter base name
  82. if (($fdst=~m|^.*/([^/]+)$|,length $1) < ($fsrc=~m|^.*/([^/]+)$|,length $1)) {
  83. $f=$fdst;
  84. } elsif (($fdst=~m|^.*/([^/]+)$|,length $1) > ($fsrc=~m|^.*/([^/]+)$|,length $1)) {
  85. $f=$fsrc;
  86. } else {
  87. # shortest names
  88. if (length $fdst < length $fsrc) {
  89. $f=$fdst;
  90. } else {
  91. $f=$fsrc;
  92. }
  93. }
  94. }
  95. }
  96. $f=$f.".diff";
  97. } elsif ($state==1 && !/^([+\- ]|@@)/) {
  98. # start of comments, end of diff contents
  99. $npos=tell(I)-length;
  100. printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $npos-$pos, datetime($time), $prefix, $f
  101. if $f;
  102. $pos=$npos;
  103. $state=0;
  104. }
  105. }
  106. $npos=tell(I);
  107. printf "-rw-r--r-- 1 %s %s %d %s %s%s\n", $uid, $gid, $npos-$pos, datetime($time), $prefix, $f
  108. if $f;
  109. }
  110. sub copyout
  111. {
  112. my ($file,$out)=@_;
  113. my ($fsrc,$fdst,$found,$state,$buf);
  114. $file=~s/^(PATCH-(CREATE|REMOVE)\/)?(.*)\.diff$/$3/;
  115. # state==1 means diff contents, state==0 mens comments
  116. $state=1; $found=0; $buf="";
  117. while (<I>) {
  118. if (/^-{3} /) {
  119. # parse diff header
  120. last if ($state==1 && $found);
  121. $state=1;
  122. error "Can't parse unified diff header"
  123. unless ((($_.=<I>).=<I>)=~/^\-{3} .*\n\+{3} .*\n@@ .* @@\n$/);
  124. ($fsrc)=/^-{3} ([^\s]+).*\n.*\n.*\n$/;
  125. ($fdst)=/^.*\n\+{3} ([^\s]+).*\n.*\n$/;
  126. $found=1 if (($fsrc eq $file) || ($fdst eq $file));
  127. } elsif ($state==1 && !/^([+\- ]|@@)/) {
  128. # start of comments, end of diff contents
  129. last if ($found);
  130. $state=0;
  131. $buf="";
  132. }
  133. $buf.=$_ if ($found || $state==0)
  134. }
  135. if ($found) {
  136. open O, "> $out";
  137. print O $buf;
  138. close O;
  139. }
  140. }
  141. sub copyin
  142. {
  143. # append diff to archive
  144. my ($archive,$name,$f)=(quotemeta $_[0],$_[1],quotemeta $_[2]);
  145. my ($cmd);
  146. error "File must have .diff or .patch extension"
  147. unless $name=~/\.(diff|patch)(\.(bz|bz2|gz|z|Z))?$/;
  148. $_=`$file $f`;
  149. if (/bzip/) {
  150. $cmd="$bzip -dc $f";
  151. } elsif (/gzip/) {
  152. $cmd="$gzip -dc $f";
  153. } else {
  154. $cmd="cat $f";
  155. }
  156. $_=`$file $archive`;
  157. if (/bzip/) {
  158. system "$cmd | $bzip -c >> $archive";
  159. } elsif (/gzip/) {
  160. system "$cmd | $gzip -c >> $archive";
  161. } else {
  162. system "$cmd >> $archive";
  163. }
  164. }
  165. sub openread
  166. {
  167. # open (compressed) archive for reading
  168. my ($archive) = (quotemeta $_[0]);
  169. $_=`$file $archive`;
  170. if (/bzip/) {
  171. open I, "$bzip -dc $ARGV[1] |";
  172. } elsif (/gzip/) {
  173. open I, "$gzip -dc $ARGV[1] |";
  174. } else {
  175. open I, "< $ARGV[1]";
  176. }
  177. }
  178. if ($ARGV[0] eq "list") {
  179. openread $ARGV[1];
  180. list $ARGV[1];
  181. exit 0;
  182. } if ($ARGV[0] eq "copyout") {
  183. openread $ARGV[1];
  184. copyout ($ARGV[2], $ARGV[3]);
  185. exit 0;
  186. } if ($ARGV[0] eq "copyin") {
  187. copyin ($ARGV[1], $ARGV[2], $ARGV[3]);
  188. exit 0;
  189. }
  190. exit 1;