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