18c2ecf20Sopenharmony_ci#!/usr/bin/env perl 28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0 38c2ecf20Sopenharmony_ci# 48c2ecf20Sopenharmony_ci# Clean a patch file -- or directory of patch files -- of stealth whitespace. 58c2ecf20Sopenharmony_ci# WARNING: this can be a highly destructive operation. Use with caution. 68c2ecf20Sopenharmony_ci# 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ciuse warnings; 98c2ecf20Sopenharmony_ciuse bytes; 108c2ecf20Sopenharmony_ciuse File::Basename; 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci# Default options 138c2ecf20Sopenharmony_ci$max_width = 79; 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci# Clean up space-tab sequences, either by removing spaces or 168c2ecf20Sopenharmony_ci# replacing them with tabs. 178c2ecf20Sopenharmony_cisub clean_space_tabs($) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci no bytes; # Tab alignment depends on characters 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci my($li) = @_; 228c2ecf20Sopenharmony_ci my($lo) = ''; 238c2ecf20Sopenharmony_ci my $pos = 0; 248c2ecf20Sopenharmony_ci my $nsp = 0; 258c2ecf20Sopenharmony_ci my($i, $c); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci for ($i = 0; $i < length($li); $i++) { 288c2ecf20Sopenharmony_ci $c = substr($li, $i, 1); 298c2ecf20Sopenharmony_ci if ($c eq "\t") { 308c2ecf20Sopenharmony_ci my $npos = ($pos+$nsp+8) & ~7; 318c2ecf20Sopenharmony_ci my $ntab = ($npos >> 3) - ($pos >> 3); 328c2ecf20Sopenharmony_ci $lo .= "\t" x $ntab; 338c2ecf20Sopenharmony_ci $pos = $npos; 348c2ecf20Sopenharmony_ci $nsp = 0; 358c2ecf20Sopenharmony_ci } elsif ($c eq "\n" || $c eq "\r") { 368c2ecf20Sopenharmony_ci $lo .= " " x $nsp; 378c2ecf20Sopenharmony_ci $pos += $nsp; 388c2ecf20Sopenharmony_ci $nsp = 0; 398c2ecf20Sopenharmony_ci $lo .= $c; 408c2ecf20Sopenharmony_ci $pos = 0; 418c2ecf20Sopenharmony_ci } elsif ($c eq " ") { 428c2ecf20Sopenharmony_ci $nsp++; 438c2ecf20Sopenharmony_ci } else { 448c2ecf20Sopenharmony_ci $lo .= " " x $nsp; 458c2ecf20Sopenharmony_ci $pos += $nsp; 468c2ecf20Sopenharmony_ci $nsp = 0; 478c2ecf20Sopenharmony_ci $lo .= $c; 488c2ecf20Sopenharmony_ci $pos++; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci $lo .= " " x $nsp; 528c2ecf20Sopenharmony_ci return $lo; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci# Compute the visual width of a string 568c2ecf20Sopenharmony_cisub strwidth($) { 578c2ecf20Sopenharmony_ci no bytes; # Tab alignment depends on characters 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci my($li) = @_; 608c2ecf20Sopenharmony_ci my($c, $i); 618c2ecf20Sopenharmony_ci my $pos = 0; 628c2ecf20Sopenharmony_ci my $mlen = 0; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci for ($i = 0; $i < length($li); $i++) { 658c2ecf20Sopenharmony_ci $c = substr($li,$i,1); 668c2ecf20Sopenharmony_ci if ($c eq "\t") { 678c2ecf20Sopenharmony_ci $pos = ($pos+8) & ~7; 688c2ecf20Sopenharmony_ci } elsif ($c eq "\n") { 698c2ecf20Sopenharmony_ci $mlen = $pos if ($pos > $mlen); 708c2ecf20Sopenharmony_ci $pos = 0; 718c2ecf20Sopenharmony_ci } else { 728c2ecf20Sopenharmony_ci $pos++; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci $mlen = $pos if ($pos > $mlen); 778c2ecf20Sopenharmony_ci return $mlen; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci$name = basename($0); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci@files = (); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ciwhile (defined($a = shift(@ARGV))) { 858c2ecf20Sopenharmony_ci if ($a =~ /^-/) { 868c2ecf20Sopenharmony_ci if ($a eq '-width' || $a eq '-w') { 878c2ecf20Sopenharmony_ci $max_width = shift(@ARGV)+0; 888c2ecf20Sopenharmony_ci } else { 898c2ecf20Sopenharmony_ci print STDERR "Usage: $name [-width #] files...\n"; 908c2ecf20Sopenharmony_ci exit 1; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci } else { 938c2ecf20Sopenharmony_ci push(@files, $a); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ciforeach $f ( @files ) { 988c2ecf20Sopenharmony_ci print STDERR "$name: $f\n"; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (! -f $f) { 1018c2ecf20Sopenharmony_ci print STDERR "$f: not a file\n"; 1028c2ecf20Sopenharmony_ci next; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (!open(FILE, '+<', $f)) { 1068c2ecf20Sopenharmony_ci print STDERR "$name: Cannot open file: $f: $!\n"; 1078c2ecf20Sopenharmony_ci next; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci binmode FILE; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci # First, verify that it is not a binary file; consider any file 1138c2ecf20Sopenharmony_ci # with a zero byte to be a binary file. Is there any better, or 1148c2ecf20Sopenharmony_ci # additional, heuristic that should be applied? 1158c2ecf20Sopenharmony_ci $is_binary = 0; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci while (read(FILE, $data, 65536) > 0) { 1188c2ecf20Sopenharmony_ci if ($data =~ /\0/) { 1198c2ecf20Sopenharmony_ci $is_binary = 1; 1208c2ecf20Sopenharmony_ci last; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if ($is_binary) { 1258c2ecf20Sopenharmony_ci print STDERR "$name: $f: binary file\n"; 1268c2ecf20Sopenharmony_ci next; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci seek(FILE, 0, 0); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci $in_bytes = 0; 1328c2ecf20Sopenharmony_ci $out_bytes = 0; 1338c2ecf20Sopenharmony_ci $lineno = 0; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci @lines = (); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci $in_hunk = 0; 1388c2ecf20Sopenharmony_ci $err = 0; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci while ( defined($line = <FILE>) ) { 1418c2ecf20Sopenharmony_ci $lineno++; 1428c2ecf20Sopenharmony_ci $in_bytes += length($line); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (!$in_hunk) { 1458c2ecf20Sopenharmony_ci if ($line =~ 1468c2ecf20Sopenharmony_ci /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@/) { 1478c2ecf20Sopenharmony_ci $minus_lines = $2; 1488c2ecf20Sopenharmony_ci $plus_lines = $4; 1498c2ecf20Sopenharmony_ci if ($minus_lines || $plus_lines) { 1508c2ecf20Sopenharmony_ci $in_hunk = 1; 1518c2ecf20Sopenharmony_ci @hunk_lines = ($line); 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } else { 1548c2ecf20Sopenharmony_ci push(@lines, $line); 1558c2ecf20Sopenharmony_ci $out_bytes += length($line); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci } else { 1588c2ecf20Sopenharmony_ci # We're in a hunk 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if ($line =~ /^\+/) { 1618c2ecf20Sopenharmony_ci $plus_lines--; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci $text = substr($line, 1); 1648c2ecf20Sopenharmony_ci $text =~ s/[ \t\r]*$//; # Remove trailing spaces 1658c2ecf20Sopenharmony_ci $text = clean_space_tabs($text); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci $l_width = strwidth($text); 1688c2ecf20Sopenharmony_ci if ($max_width && $l_width > $max_width) { 1698c2ecf20Sopenharmony_ci print STDERR 1708c2ecf20Sopenharmony_ci "$f:$lineno: adds line exceeds $max_width ", 1718c2ecf20Sopenharmony_ci "characters ($l_width)\n"; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci push(@hunk_lines, '+'.$text); 1758c2ecf20Sopenharmony_ci } elsif ($line =~ /^\-/) { 1768c2ecf20Sopenharmony_ci $minus_lines--; 1778c2ecf20Sopenharmony_ci push(@hunk_lines, $line); 1788c2ecf20Sopenharmony_ci } elsif ($line =~ /^ /) { 1798c2ecf20Sopenharmony_ci $plus_lines--; 1808c2ecf20Sopenharmony_ci $minus_lines--; 1818c2ecf20Sopenharmony_ci push(@hunk_lines, $line); 1828c2ecf20Sopenharmony_ci } else { 1838c2ecf20Sopenharmony_ci print STDERR "$name: $f: malformed patch\n"; 1848c2ecf20Sopenharmony_ci $err = 1; 1858c2ecf20Sopenharmony_ci last; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if ($plus_lines < 0 || $minus_lines < 0) { 1898c2ecf20Sopenharmony_ci print STDERR "$name: $f: malformed patch\n"; 1908c2ecf20Sopenharmony_ci $err = 1; 1918c2ecf20Sopenharmony_ci last; 1928c2ecf20Sopenharmony_ci } elsif ($plus_lines == 0 && $minus_lines == 0) { 1938c2ecf20Sopenharmony_ci # End of a hunk. Process this hunk. 1948c2ecf20Sopenharmony_ci my $i; 1958c2ecf20Sopenharmony_ci my $l; 1968c2ecf20Sopenharmony_ci my @h = (); 1978c2ecf20Sopenharmony_ci my $adj = 0; 1988c2ecf20Sopenharmony_ci my $done = 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci for ($i = scalar(@hunk_lines)-1; $i > 0; $i--) { 2018c2ecf20Sopenharmony_ci $l = $hunk_lines[$i]; 2028c2ecf20Sopenharmony_ci if (!$done && $l eq "+\n") { 2038c2ecf20Sopenharmony_ci $adj++; # Skip this line 2048c2ecf20Sopenharmony_ci } elsif ($l =~ /^[ +]/) { 2058c2ecf20Sopenharmony_ci $done = 1; 2068c2ecf20Sopenharmony_ci unshift(@h, $l); 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci unshift(@h, $l); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci $l = $hunk_lines[0]; # Hunk header 2138c2ecf20Sopenharmony_ci undef @hunk_lines; # Free memory 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if ($adj) { 2168c2ecf20Sopenharmony_ci die unless 2178c2ecf20Sopenharmony_ci ($l =~ /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@(.*)$/); 2188c2ecf20Sopenharmony_ci my $mstart = $1; 2198c2ecf20Sopenharmony_ci my $mlin = $2; 2208c2ecf20Sopenharmony_ci my $pstart = $3; 2218c2ecf20Sopenharmony_ci my $plin = $4; 2228c2ecf20Sopenharmony_ci my $tail = $5; # doesn't include the final newline 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci $l = sprintf("@@ -%d,%d +%d,%d @@%s\n", 2258c2ecf20Sopenharmony_ci $mstart, $mlin, $pstart, $plin-$adj, 2268c2ecf20Sopenharmony_ci $tail); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci unshift(@h, $l); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci # Transfer to the output array 2318c2ecf20Sopenharmony_ci foreach $l (@h) { 2328c2ecf20Sopenharmony_ci $out_bytes += length($l); 2338c2ecf20Sopenharmony_ci push(@lines, $l); 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci $in_hunk = 0; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if ($in_hunk) { 2428c2ecf20Sopenharmony_ci print STDERR "$name: $f: malformed patch\n"; 2438c2ecf20Sopenharmony_ci $err = 1; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (!$err) { 2478c2ecf20Sopenharmony_ci if ($in_bytes != $out_bytes) { 2488c2ecf20Sopenharmony_ci # Only write to the file if changed 2498c2ecf20Sopenharmony_ci seek(FILE, 0, 0); 2508c2ecf20Sopenharmony_ci print FILE @lines; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if ( !defined($where = tell(FILE)) || 2538c2ecf20Sopenharmony_ci !truncate(FILE, $where) ) { 2548c2ecf20Sopenharmony_ci die "$name: Failed to truncate modified file: $f: $!\n"; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci close(FILE); 2608c2ecf20Sopenharmony_ci} 261