1*53ee8cc1Swenshuai.xi#!/usr/bin/perl -w 2*53ee8cc1Swenshuai.xi# 3*53ee8cc1Swenshuai.xi# Clean a patch file -- or directory of patch files -- of stealth whitespace. 4*53ee8cc1Swenshuai.xi# WARNING: this can be a highly destructive operation. Use with caution. 5*53ee8cc1Swenshuai.xi# 6*53ee8cc1Swenshuai.xi 7*53ee8cc1Swenshuai.xiuse bytes; 8*53ee8cc1Swenshuai.xiuse File::Basename; 9*53ee8cc1Swenshuai.xi 10*53ee8cc1Swenshuai.xi# Default options 11*53ee8cc1Swenshuai.xi$max_width = 79; 12*53ee8cc1Swenshuai.xi 13*53ee8cc1Swenshuai.xi# Clean up space-tab sequences, either by removing spaces or 14*53ee8cc1Swenshuai.xi# replacing them with tabs. 15*53ee8cc1Swenshuai.xisub clean_space_tabs($) 16*53ee8cc1Swenshuai.xi{ 17*53ee8cc1Swenshuai.xi no bytes; # Tab alignment depends on characters 18*53ee8cc1Swenshuai.xi 19*53ee8cc1Swenshuai.xi my($li) = @_; 20*53ee8cc1Swenshuai.xi my($lo) = ''; 21*53ee8cc1Swenshuai.xi my $pos = 0; 22*53ee8cc1Swenshuai.xi my $nsp = 0; 23*53ee8cc1Swenshuai.xi my($i, $c); 24*53ee8cc1Swenshuai.xi 25*53ee8cc1Swenshuai.xi for ($i = 0; $i < length($li); $i++) { 26*53ee8cc1Swenshuai.xi $c = substr($li, $i, 1); 27*53ee8cc1Swenshuai.xi if ($c eq "\t") { 28*53ee8cc1Swenshuai.xi my $npos = ($pos+$nsp+8) & ~7; 29*53ee8cc1Swenshuai.xi my $ntab = ($npos >> 3) - ($pos >> 3); 30*53ee8cc1Swenshuai.xi $lo .= "\t" x $ntab; 31*53ee8cc1Swenshuai.xi $pos = $npos; 32*53ee8cc1Swenshuai.xi $nsp = 0; 33*53ee8cc1Swenshuai.xi } elsif ($c eq "\n" || $c eq "\r") { 34*53ee8cc1Swenshuai.xi $lo .= " " x $nsp; 35*53ee8cc1Swenshuai.xi $pos += $nsp; 36*53ee8cc1Swenshuai.xi $nsp = 0; 37*53ee8cc1Swenshuai.xi $lo .= $c; 38*53ee8cc1Swenshuai.xi $pos = 0; 39*53ee8cc1Swenshuai.xi } elsif ($c eq " ") { 40*53ee8cc1Swenshuai.xi $nsp++; 41*53ee8cc1Swenshuai.xi } else { 42*53ee8cc1Swenshuai.xi $lo .= " " x $nsp; 43*53ee8cc1Swenshuai.xi $pos += $nsp; 44*53ee8cc1Swenshuai.xi $nsp = 0; 45*53ee8cc1Swenshuai.xi $lo .= $c; 46*53ee8cc1Swenshuai.xi $pos++; 47*53ee8cc1Swenshuai.xi } 48*53ee8cc1Swenshuai.xi } 49*53ee8cc1Swenshuai.xi $lo .= " " x $nsp; 50*53ee8cc1Swenshuai.xi return $lo; 51*53ee8cc1Swenshuai.xi} 52*53ee8cc1Swenshuai.xi 53*53ee8cc1Swenshuai.xi# Compute the visual width of a string 54*53ee8cc1Swenshuai.xisub strwidth($) { 55*53ee8cc1Swenshuai.xi no bytes; # Tab alignment depends on characters 56*53ee8cc1Swenshuai.xi 57*53ee8cc1Swenshuai.xi my($li) = @_; 58*53ee8cc1Swenshuai.xi my($c, $i); 59*53ee8cc1Swenshuai.xi my $pos = 0; 60*53ee8cc1Swenshuai.xi my $mlen = 0; 61*53ee8cc1Swenshuai.xi 62*53ee8cc1Swenshuai.xi for ($i = 0; $i < length($li); $i++) { 63*53ee8cc1Swenshuai.xi $c = substr($li,$i,1); 64*53ee8cc1Swenshuai.xi if ($c eq "\t") { 65*53ee8cc1Swenshuai.xi $pos = ($pos+8) & ~7; 66*53ee8cc1Swenshuai.xi } elsif ($c eq "\n") { 67*53ee8cc1Swenshuai.xi $mlen = $pos if ($pos > $mlen); 68*53ee8cc1Swenshuai.xi $pos = 0; 69*53ee8cc1Swenshuai.xi } else { 70*53ee8cc1Swenshuai.xi $pos++; 71*53ee8cc1Swenshuai.xi } 72*53ee8cc1Swenshuai.xi } 73*53ee8cc1Swenshuai.xi 74*53ee8cc1Swenshuai.xi $mlen = $pos if ($pos > $mlen); 75*53ee8cc1Swenshuai.xi return $mlen; 76*53ee8cc1Swenshuai.xi} 77*53ee8cc1Swenshuai.xi 78*53ee8cc1Swenshuai.xi$name = basename($0); 79*53ee8cc1Swenshuai.xi 80*53ee8cc1Swenshuai.xi@files = (); 81*53ee8cc1Swenshuai.xi 82*53ee8cc1Swenshuai.xiwhile (defined($a = shift(@ARGV))) { 83*53ee8cc1Swenshuai.xi if ($a =~ /^-/) { 84*53ee8cc1Swenshuai.xi if ($a eq '-width' || $a eq '-w') { 85*53ee8cc1Swenshuai.xi $max_width = shift(@ARGV)+0; 86*53ee8cc1Swenshuai.xi } else { 87*53ee8cc1Swenshuai.xi print STDERR "Usage: $name [-width #] files...\n"; 88*53ee8cc1Swenshuai.xi exit 1; 89*53ee8cc1Swenshuai.xi } 90*53ee8cc1Swenshuai.xi } else { 91*53ee8cc1Swenshuai.xi push(@files, $a); 92*53ee8cc1Swenshuai.xi } 93*53ee8cc1Swenshuai.xi} 94*53ee8cc1Swenshuai.xi 95*53ee8cc1Swenshuai.xiforeach $f ( @files ) { 96*53ee8cc1Swenshuai.xi print STDERR "$name: $f\n"; 97*53ee8cc1Swenshuai.xi 98*53ee8cc1Swenshuai.xi if (! -f $f) { 99*53ee8cc1Swenshuai.xi print STDERR "$f: not a file\n"; 100*53ee8cc1Swenshuai.xi next; 101*53ee8cc1Swenshuai.xi } 102*53ee8cc1Swenshuai.xi 103*53ee8cc1Swenshuai.xi if (!open(FILE, '+<', $f)) { 104*53ee8cc1Swenshuai.xi print STDERR "$name: Cannot open file: $f: $!\n"; 105*53ee8cc1Swenshuai.xi next; 106*53ee8cc1Swenshuai.xi } 107*53ee8cc1Swenshuai.xi 108*53ee8cc1Swenshuai.xi binmode FILE; 109*53ee8cc1Swenshuai.xi 110*53ee8cc1Swenshuai.xi # First, verify that it is not a binary file; consider any file 111*53ee8cc1Swenshuai.xi # with a zero byte to be a binary file. Is there any better, or 112*53ee8cc1Swenshuai.xi # additional, heuristic that should be applied? 113*53ee8cc1Swenshuai.xi $is_binary = 0; 114*53ee8cc1Swenshuai.xi 115*53ee8cc1Swenshuai.xi while (read(FILE, $data, 65536) > 0) { 116*53ee8cc1Swenshuai.xi if ($data =~ /\0/) { 117*53ee8cc1Swenshuai.xi $is_binary = 1; 118*53ee8cc1Swenshuai.xi last; 119*53ee8cc1Swenshuai.xi } 120*53ee8cc1Swenshuai.xi } 121*53ee8cc1Swenshuai.xi 122*53ee8cc1Swenshuai.xi if ($is_binary) { 123*53ee8cc1Swenshuai.xi print STDERR "$name: $f: binary file\n"; 124*53ee8cc1Swenshuai.xi next; 125*53ee8cc1Swenshuai.xi } 126*53ee8cc1Swenshuai.xi 127*53ee8cc1Swenshuai.xi seek(FILE, 0, 0); 128*53ee8cc1Swenshuai.xi 129*53ee8cc1Swenshuai.xi $in_bytes = 0; 130*53ee8cc1Swenshuai.xi $out_bytes = 0; 131*53ee8cc1Swenshuai.xi $lineno = 0; 132*53ee8cc1Swenshuai.xi 133*53ee8cc1Swenshuai.xi @lines = (); 134*53ee8cc1Swenshuai.xi 135*53ee8cc1Swenshuai.xi $in_hunk = 0; 136*53ee8cc1Swenshuai.xi $err = 0; 137*53ee8cc1Swenshuai.xi 138*53ee8cc1Swenshuai.xi while ( defined($line = <FILE>) ) { 139*53ee8cc1Swenshuai.xi $lineno++; 140*53ee8cc1Swenshuai.xi $in_bytes += length($line); 141*53ee8cc1Swenshuai.xi 142*53ee8cc1Swenshuai.xi if (!$in_hunk) { 143*53ee8cc1Swenshuai.xi if ($line =~ 144*53ee8cc1Swenshuai.xi /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@/) { 145*53ee8cc1Swenshuai.xi $minus_lines = $2; 146*53ee8cc1Swenshuai.xi $plus_lines = $4; 147*53ee8cc1Swenshuai.xi if ($minus_lines || $plus_lines) { 148*53ee8cc1Swenshuai.xi $in_hunk = 1; 149*53ee8cc1Swenshuai.xi @hunk_lines = ($line); 150*53ee8cc1Swenshuai.xi } 151*53ee8cc1Swenshuai.xi } else { 152*53ee8cc1Swenshuai.xi push(@lines, $line); 153*53ee8cc1Swenshuai.xi $out_bytes += length($line); 154*53ee8cc1Swenshuai.xi } 155*53ee8cc1Swenshuai.xi } else { 156*53ee8cc1Swenshuai.xi # We're in a hunk 157*53ee8cc1Swenshuai.xi 158*53ee8cc1Swenshuai.xi if ($line =~ /^\+/) { 159*53ee8cc1Swenshuai.xi $plus_lines--; 160*53ee8cc1Swenshuai.xi 161*53ee8cc1Swenshuai.xi $text = substr($line, 1); 162*53ee8cc1Swenshuai.xi $text =~ s/[ \t\r]*$//; # Remove trailing spaces 163*53ee8cc1Swenshuai.xi $text = clean_space_tabs($text); 164*53ee8cc1Swenshuai.xi 165*53ee8cc1Swenshuai.xi $l_width = strwidth($text); 166*53ee8cc1Swenshuai.xi if ($max_width && $l_width > $max_width) { 167*53ee8cc1Swenshuai.xi print STDERR 168*53ee8cc1Swenshuai.xi "$f:$lineno: adds line exceeds $max_width ", 169*53ee8cc1Swenshuai.xi "characters ($l_width)\n"; 170*53ee8cc1Swenshuai.xi } 171*53ee8cc1Swenshuai.xi 172*53ee8cc1Swenshuai.xi push(@hunk_lines, '+'.$text); 173*53ee8cc1Swenshuai.xi } elsif ($line =~ /^\-/) { 174*53ee8cc1Swenshuai.xi $minus_lines--; 175*53ee8cc1Swenshuai.xi push(@hunk_lines, $line); 176*53ee8cc1Swenshuai.xi } elsif ($line =~ /^ /) { 177*53ee8cc1Swenshuai.xi $plus_lines--; 178*53ee8cc1Swenshuai.xi $minus_lines--; 179*53ee8cc1Swenshuai.xi push(@hunk_lines, $line); 180*53ee8cc1Swenshuai.xi } else { 181*53ee8cc1Swenshuai.xi print STDERR "$name: $f: malformed patch\n"; 182*53ee8cc1Swenshuai.xi $err = 1; 183*53ee8cc1Swenshuai.xi last; 184*53ee8cc1Swenshuai.xi } 185*53ee8cc1Swenshuai.xi 186*53ee8cc1Swenshuai.xi if ($plus_lines < 0 || $minus_lines < 0) { 187*53ee8cc1Swenshuai.xi print STDERR "$name: $f: malformed patch\n"; 188*53ee8cc1Swenshuai.xi $err = 1; 189*53ee8cc1Swenshuai.xi last; 190*53ee8cc1Swenshuai.xi } elsif ($plus_lines == 0 && $minus_lines == 0) { 191*53ee8cc1Swenshuai.xi # End of a hunk. Process this hunk. 192*53ee8cc1Swenshuai.xi my $i; 193*53ee8cc1Swenshuai.xi my $l; 194*53ee8cc1Swenshuai.xi my @h = (); 195*53ee8cc1Swenshuai.xi my $adj = 0; 196*53ee8cc1Swenshuai.xi my $done = 0; 197*53ee8cc1Swenshuai.xi 198*53ee8cc1Swenshuai.xi for ($i = scalar(@hunk_lines)-1; $i > 0; $i--) { 199*53ee8cc1Swenshuai.xi $l = $hunk_lines[$i]; 200*53ee8cc1Swenshuai.xi if (!$done && $l eq "+\n") { 201*53ee8cc1Swenshuai.xi $adj++; # Skip this line 202*53ee8cc1Swenshuai.xi } elsif ($l =~ /^[ +]/) { 203*53ee8cc1Swenshuai.xi $done = 1; 204*53ee8cc1Swenshuai.xi unshift(@h, $l); 205*53ee8cc1Swenshuai.xi } else { 206*53ee8cc1Swenshuai.xi unshift(@h, $l); 207*53ee8cc1Swenshuai.xi } 208*53ee8cc1Swenshuai.xi } 209*53ee8cc1Swenshuai.xi 210*53ee8cc1Swenshuai.xi $l = $hunk_lines[0]; # Hunk header 211*53ee8cc1Swenshuai.xi undef @hunk_lines; # Free memory 212*53ee8cc1Swenshuai.xi 213*53ee8cc1Swenshuai.xi if ($adj) { 214*53ee8cc1Swenshuai.xi die unless 215*53ee8cc1Swenshuai.xi ($l =~ /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@(.*)$/); 216*53ee8cc1Swenshuai.xi my $mstart = $1; 217*53ee8cc1Swenshuai.xi my $mlin = $2; 218*53ee8cc1Swenshuai.xi my $pstart = $3; 219*53ee8cc1Swenshuai.xi my $plin = $4; 220*53ee8cc1Swenshuai.xi my $tail = $5; # doesn't include the final newline 221*53ee8cc1Swenshuai.xi 222*53ee8cc1Swenshuai.xi $l = sprintf("@@ -%d,%d +%d,%d @@%s\n", 223*53ee8cc1Swenshuai.xi $mstart, $mlin, $pstart, $plin-$adj, 224*53ee8cc1Swenshuai.xi $tail); 225*53ee8cc1Swenshuai.xi } 226*53ee8cc1Swenshuai.xi unshift(@h, $l); 227*53ee8cc1Swenshuai.xi 228*53ee8cc1Swenshuai.xi # Transfer to the output array 229*53ee8cc1Swenshuai.xi foreach $l (@h) { 230*53ee8cc1Swenshuai.xi $out_bytes += length($l); 231*53ee8cc1Swenshuai.xi push(@lines, $l); 232*53ee8cc1Swenshuai.xi } 233*53ee8cc1Swenshuai.xi 234*53ee8cc1Swenshuai.xi $in_hunk = 0; 235*53ee8cc1Swenshuai.xi } 236*53ee8cc1Swenshuai.xi } 237*53ee8cc1Swenshuai.xi } 238*53ee8cc1Swenshuai.xi 239*53ee8cc1Swenshuai.xi if ($in_hunk) { 240*53ee8cc1Swenshuai.xi print STDERR "$name: $f: malformed patch\n"; 241*53ee8cc1Swenshuai.xi $err = 1; 242*53ee8cc1Swenshuai.xi } 243*53ee8cc1Swenshuai.xi 244*53ee8cc1Swenshuai.xi if (!$err) { 245*53ee8cc1Swenshuai.xi if ($in_bytes != $out_bytes) { 246*53ee8cc1Swenshuai.xi # Only write to the file if changed 247*53ee8cc1Swenshuai.xi seek(FILE, 0, 0); 248*53ee8cc1Swenshuai.xi print FILE @lines; 249*53ee8cc1Swenshuai.xi 250*53ee8cc1Swenshuai.xi if ( !defined($where = tell(FILE)) || 251*53ee8cc1Swenshuai.xi !truncate(FILE, $where) ) { 252*53ee8cc1Swenshuai.xi die "$name: Failed to truncate modified file: $f: $!\n"; 253*53ee8cc1Swenshuai.xi } 254*53ee8cc1Swenshuai.xi } 255*53ee8cc1Swenshuai.xi } 256*53ee8cc1Swenshuai.xi 257*53ee8cc1Swenshuai.xi close(FILE); 258*53ee8cc1Swenshuai.xi} 259