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