1cb77f0d6SKamil Rytarowski#!/usr/bin/env perl 2*b2441318SGreg Kroah-Hartman# SPDX-License-Identifier: GPL-2.0 36e019b00SH. Peter Anvin# 46e019b00SH. Peter Anvin# Clean a patch file -- or directory of patch files -- of stealth whitespace. 56e019b00SH. Peter Anvin# WARNING: this can be a highly destructive operation. Use with caution. 66e019b00SH. Peter Anvin# 76e019b00SH. Peter Anvin 8cb77f0d6SKamil Rytarowskiuse warnings; 96e019b00SH. Peter Anvinuse bytes; 106e019b00SH. Peter Anvinuse File::Basename; 116e019b00SH. Peter Anvin 12cb3ed5b7SH. Peter Anvin# Default options 13cb3ed5b7SH. Peter Anvin$max_width = 79; 14cb3ed5b7SH. Peter Anvin 156e019b00SH. Peter Anvin# Clean up space-tab sequences, either by removing spaces or 166e019b00SH. Peter Anvin# replacing them with tabs. 176e019b00SH. Peter Anvinsub clean_space_tabs($) 186e019b00SH. Peter Anvin{ 196e019b00SH. Peter Anvin no bytes; # Tab alignment depends on characters 206e019b00SH. Peter Anvin 216e019b00SH. Peter Anvin my($li) = @_; 226e019b00SH. Peter Anvin my($lo) = ''; 236e019b00SH. Peter Anvin my $pos = 0; 246e019b00SH. Peter Anvin my $nsp = 0; 256e019b00SH. Peter Anvin my($i, $c); 266e019b00SH. Peter Anvin 276e019b00SH. Peter Anvin for ($i = 0; $i < length($li); $i++) { 286e019b00SH. Peter Anvin $c = substr($li, $i, 1); 296e019b00SH. Peter Anvin if ($c eq "\t") { 306e019b00SH. Peter Anvin my $npos = ($pos+$nsp+8) & ~7; 316e019b00SH. Peter Anvin my $ntab = ($npos >> 3) - ($pos >> 3); 326e019b00SH. Peter Anvin $lo .= "\t" x $ntab; 336e019b00SH. Peter Anvin $pos = $npos; 346e019b00SH. Peter Anvin $nsp = 0; 356e019b00SH. Peter Anvin } elsif ($c eq "\n" || $c eq "\r") { 366e019b00SH. Peter Anvin $lo .= " " x $nsp; 376e019b00SH. Peter Anvin $pos += $nsp; 386e019b00SH. Peter Anvin $nsp = 0; 396e019b00SH. Peter Anvin $lo .= $c; 406e019b00SH. Peter Anvin $pos = 0; 416e019b00SH. Peter Anvin } elsif ($c eq " ") { 426e019b00SH. Peter Anvin $nsp++; 436e019b00SH. Peter Anvin } else { 446e019b00SH. Peter Anvin $lo .= " " x $nsp; 456e019b00SH. Peter Anvin $pos += $nsp; 466e019b00SH. Peter Anvin $nsp = 0; 476e019b00SH. Peter Anvin $lo .= $c; 486e019b00SH. Peter Anvin $pos++; 496e019b00SH. Peter Anvin } 506e019b00SH. Peter Anvin } 516e019b00SH. Peter Anvin $lo .= " " x $nsp; 526e019b00SH. Peter Anvin return $lo; 536e019b00SH. Peter Anvin} 546e019b00SH. Peter Anvin 55cb3ed5b7SH. Peter Anvin# Compute the visual width of a string 56cb3ed5b7SH. Peter Anvinsub strwidth($) { 57cb3ed5b7SH. Peter Anvin no bytes; # Tab alignment depends on characters 58cb3ed5b7SH. Peter Anvin 59cb3ed5b7SH. Peter Anvin my($li) = @_; 60cb3ed5b7SH. Peter Anvin my($c, $i); 61cb3ed5b7SH. Peter Anvin my $pos = 0; 62cb3ed5b7SH. Peter Anvin my $mlen = 0; 63cb3ed5b7SH. Peter Anvin 64cb3ed5b7SH. Peter Anvin for ($i = 0; $i < length($li); $i++) { 65cb3ed5b7SH. Peter Anvin $c = substr($li,$i,1); 66cb3ed5b7SH. Peter Anvin if ($c eq "\t") { 67cb3ed5b7SH. Peter Anvin $pos = ($pos+8) & ~7; 68cb3ed5b7SH. Peter Anvin } elsif ($c eq "\n") { 69cb3ed5b7SH. Peter Anvin $mlen = $pos if ($pos > $mlen); 70cb3ed5b7SH. Peter Anvin $pos = 0; 71cb3ed5b7SH. Peter Anvin } else { 72cb3ed5b7SH. Peter Anvin $pos++; 73cb3ed5b7SH. Peter Anvin } 74cb3ed5b7SH. Peter Anvin } 75cb3ed5b7SH. Peter Anvin 76cb3ed5b7SH. Peter Anvin $mlen = $pos if ($pos > $mlen); 77cb3ed5b7SH. Peter Anvin return $mlen; 78cb3ed5b7SH. Peter Anvin} 79cb3ed5b7SH. Peter Anvin 806e019b00SH. Peter Anvin$name = basename($0); 816e019b00SH. Peter Anvin 82cb3ed5b7SH. Peter Anvin@files = (); 83cb3ed5b7SH. Peter Anvin 84cb3ed5b7SH. Peter Anvinwhile (defined($a = shift(@ARGV))) { 85cb3ed5b7SH. Peter Anvin if ($a =~ /^-/) { 86cb3ed5b7SH. Peter Anvin if ($a eq '-width' || $a eq '-w') { 87cb3ed5b7SH. Peter Anvin $max_width = shift(@ARGV)+0; 88cb3ed5b7SH. Peter Anvin } else { 89cb3ed5b7SH. Peter Anvin print STDERR "Usage: $name [-width #] files...\n"; 90cb3ed5b7SH. Peter Anvin exit 1; 91cb3ed5b7SH. Peter Anvin } 92cb3ed5b7SH. Peter Anvin } else { 93cb3ed5b7SH. Peter Anvin push(@files, $a); 94cb3ed5b7SH. Peter Anvin } 95cb3ed5b7SH. Peter Anvin} 96cb3ed5b7SH. Peter Anvin 97cb3ed5b7SH. Peter Anvinforeach $f ( @files ) { 986e019b00SH. Peter Anvin print STDERR "$name: $f\n"; 996e019b00SH. Peter Anvin 1006e019b00SH. Peter Anvin if (! -f $f) { 1016e019b00SH. Peter Anvin print STDERR "$f: not a file\n"; 1026e019b00SH. Peter Anvin next; 1036e019b00SH. Peter Anvin } 1046e019b00SH. Peter Anvin 1056e019b00SH. Peter Anvin if (!open(FILE, '+<', $f)) { 1066e019b00SH. Peter Anvin print STDERR "$name: Cannot open file: $f: $!\n"; 1076e019b00SH. Peter Anvin next; 1086e019b00SH. Peter Anvin } 1096e019b00SH. Peter Anvin 1106e019b00SH. Peter Anvin binmode FILE; 1116e019b00SH. Peter Anvin 1126e019b00SH. Peter Anvin # First, verify that it is not a binary file; consider any file 1136e019b00SH. Peter Anvin # with a zero byte to be a binary file. Is there any better, or 1146e019b00SH. Peter Anvin # additional, heuristic that should be applied? 1156e019b00SH. Peter Anvin $is_binary = 0; 1166e019b00SH. Peter Anvin 1176e019b00SH. Peter Anvin while (read(FILE, $data, 65536) > 0) { 1186e019b00SH. Peter Anvin if ($data =~ /\0/) { 1196e019b00SH. Peter Anvin $is_binary = 1; 1206e019b00SH. Peter Anvin last; 1216e019b00SH. Peter Anvin } 1226e019b00SH. Peter Anvin } 1236e019b00SH. Peter Anvin 1246e019b00SH. Peter Anvin if ($is_binary) { 1256e019b00SH. Peter Anvin print STDERR "$name: $f: binary file\n"; 1266e019b00SH. Peter Anvin next; 1276e019b00SH. Peter Anvin } 1286e019b00SH. Peter Anvin 1296e019b00SH. Peter Anvin seek(FILE, 0, 0); 1306e019b00SH. Peter Anvin 1316e019b00SH. Peter Anvin $in_bytes = 0; 1326e019b00SH. Peter Anvin $out_bytes = 0; 133cb3ed5b7SH. Peter Anvin $lineno = 0; 1346e019b00SH. Peter Anvin 1356e019b00SH. Peter Anvin @lines = (); 1366e019b00SH. Peter Anvin 1376e019b00SH. Peter Anvin $in_hunk = 0; 1386e019b00SH. Peter Anvin $err = 0; 1396e019b00SH. Peter Anvin 1406e019b00SH. Peter Anvin while ( defined($line = <FILE>) ) { 141cb3ed5b7SH. Peter Anvin $lineno++; 1426e019b00SH. Peter Anvin $in_bytes += length($line); 1436e019b00SH. Peter Anvin 1446e019b00SH. Peter Anvin if (!$in_hunk) { 145cb3ed5b7SH. Peter Anvin if ($line =~ 146cb3ed5b7SH. Peter Anvin /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@/) { 1476e019b00SH. Peter Anvin $minus_lines = $2; 1486e019b00SH. Peter Anvin $plus_lines = $4; 1496e019b00SH. Peter Anvin if ($minus_lines || $plus_lines) { 1506e019b00SH. Peter Anvin $in_hunk = 1; 1516e019b00SH. Peter Anvin @hunk_lines = ($line); 1526e019b00SH. Peter Anvin } 1536e019b00SH. Peter Anvin } else { 1546e019b00SH. Peter Anvin push(@lines, $line); 1556e019b00SH. Peter Anvin $out_bytes += length($line); 1566e019b00SH. Peter Anvin } 1576e019b00SH. Peter Anvin } else { 1586e019b00SH. Peter Anvin # We're in a hunk 1596e019b00SH. Peter Anvin 1606e019b00SH. Peter Anvin if ($line =~ /^\+/) { 1616e019b00SH. Peter Anvin $plus_lines--; 1626e019b00SH. Peter Anvin 1636e019b00SH. Peter Anvin $text = substr($line, 1); 1646e019b00SH. Peter Anvin $text =~ s/[ \t\r]*$//; # Remove trailing spaces 1656e019b00SH. Peter Anvin $text = clean_space_tabs($text); 1666e019b00SH. Peter Anvin 167cb3ed5b7SH. Peter Anvin $l_width = strwidth($text); 168cb3ed5b7SH. Peter Anvin if ($max_width && $l_width > $max_width) { 169cb3ed5b7SH. Peter Anvin print STDERR 170cb3ed5b7SH. Peter Anvin "$f:$lineno: adds line exceeds $max_width ", 171cb3ed5b7SH. Peter Anvin "characters ($l_width)\n"; 172cb3ed5b7SH. Peter Anvin } 173cb3ed5b7SH. Peter Anvin 1746e019b00SH. Peter Anvin push(@hunk_lines, '+'.$text); 1756e019b00SH. Peter Anvin } elsif ($line =~ /^\-/) { 1766e019b00SH. Peter Anvin $minus_lines--; 1776e019b00SH. Peter Anvin push(@hunk_lines, $line); 1786e019b00SH. Peter Anvin } elsif ($line =~ /^ /) { 1796e019b00SH. Peter Anvin $plus_lines--; 1806e019b00SH. Peter Anvin $minus_lines--; 1816e019b00SH. Peter Anvin push(@hunk_lines, $line); 1826e019b00SH. Peter Anvin } else { 1836e019b00SH. Peter Anvin print STDERR "$name: $f: malformed patch\n"; 1846e019b00SH. Peter Anvin $err = 1; 1856e019b00SH. Peter Anvin last; 1866e019b00SH. Peter Anvin } 1876e019b00SH. Peter Anvin 1886e019b00SH. Peter Anvin if ($plus_lines < 0 || $minus_lines < 0) { 1896e019b00SH. Peter Anvin print STDERR "$name: $f: malformed patch\n"; 1906e019b00SH. Peter Anvin $err = 1; 1916e019b00SH. Peter Anvin last; 1926e019b00SH. Peter Anvin } elsif ($plus_lines == 0 && $minus_lines == 0) { 1936e019b00SH. Peter Anvin # End of a hunk. Process this hunk. 1946e019b00SH. Peter Anvin my $i; 1956e019b00SH. Peter Anvin my $l; 1966e019b00SH. Peter Anvin my @h = (); 1976e019b00SH. Peter Anvin my $adj = 0; 1986e019b00SH. Peter Anvin my $done = 0; 1996e019b00SH. Peter Anvin 2006e019b00SH. Peter Anvin for ($i = scalar(@hunk_lines)-1; $i > 0; $i--) { 2016e019b00SH. Peter Anvin $l = $hunk_lines[$i]; 2026e019b00SH. Peter Anvin if (!$done && $l eq "+\n") { 2036e019b00SH. Peter Anvin $adj++; # Skip this line 2046e019b00SH. Peter Anvin } elsif ($l =~ /^[ +]/) { 2056e019b00SH. Peter Anvin $done = 1; 2066e019b00SH. Peter Anvin unshift(@h, $l); 2076e019b00SH. Peter Anvin } else { 2086e019b00SH. Peter Anvin unshift(@h, $l); 2096e019b00SH. Peter Anvin } 2106e019b00SH. Peter Anvin } 2116e019b00SH. Peter Anvin 2126e019b00SH. Peter Anvin $l = $hunk_lines[0]; # Hunk header 2136e019b00SH. Peter Anvin undef @hunk_lines; # Free memory 2146e019b00SH. Peter Anvin 2156e019b00SH. Peter Anvin if ($adj) { 2166e019b00SH. Peter Anvin die unless 2176e019b00SH. Peter Anvin ($l =~ /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@(.*)$/); 2186e019b00SH. Peter Anvin my $mstart = $1; 2196e019b00SH. Peter Anvin my $mlin = $2; 2206e019b00SH. Peter Anvin my $pstart = $3; 2216e019b00SH. Peter Anvin my $plin = $4; 2226e019b00SH. Peter Anvin my $tail = $5; # doesn't include the final newline 2236e019b00SH. Peter Anvin 2246e019b00SH. Peter Anvin $l = sprintf("@@ -%d,%d +%d,%d @@%s\n", 2256e019b00SH. Peter Anvin $mstart, $mlin, $pstart, $plin-$adj, 2266e019b00SH. Peter Anvin $tail); 2276e019b00SH. Peter Anvin } 2286e019b00SH. Peter Anvin unshift(@h, $l); 2296e019b00SH. Peter Anvin 2306e019b00SH. Peter Anvin # Transfer to the output array 2316e019b00SH. Peter Anvin foreach $l (@h) { 2326e019b00SH. Peter Anvin $out_bytes += length($l); 2336e019b00SH. Peter Anvin push(@lines, $l); 2346e019b00SH. Peter Anvin } 2356e019b00SH. Peter Anvin 2366e019b00SH. Peter Anvin $in_hunk = 0; 2376e019b00SH. Peter Anvin } 2386e019b00SH. Peter Anvin } 2396e019b00SH. Peter Anvin } 2406e019b00SH. Peter Anvin 2416e019b00SH. Peter Anvin if ($in_hunk) { 2426e019b00SH. Peter Anvin print STDERR "$name: $f: malformed patch\n"; 2436e019b00SH. Peter Anvin $err = 1; 2446e019b00SH. Peter Anvin } 2456e019b00SH. Peter Anvin 2466e019b00SH. Peter Anvin if (!$err) { 2476e019b00SH. Peter Anvin if ($in_bytes != $out_bytes) { 2486e019b00SH. Peter Anvin # Only write to the file if changed 2496e019b00SH. Peter Anvin seek(FILE, 0, 0); 2506e019b00SH. Peter Anvin print FILE @lines; 2516e019b00SH. Peter Anvin 2526e019b00SH. Peter Anvin if ( !defined($where = tell(FILE)) || 2536e019b00SH. Peter Anvin !truncate(FILE, $where) ) { 2546e019b00SH. Peter Anvin die "$name: Failed to truncate modified file: $f: $!\n"; 2556e019b00SH. Peter Anvin } 2566e019b00SH. Peter Anvin } 2576e019b00SH. Peter Anvin } 2586e019b00SH. Peter Anvin 2596e019b00SH. Peter Anvin close(FILE); 2606e019b00SH. Peter Anvin} 261