Back to home page

LXR

 
 

    


0001 #!/usr/bin/perl -w
0002 #
0003 # Clean a patch file -- or directory of patch files -- of stealth whitespace.
0004 # WARNING: this can be a highly destructive operation.  Use with caution.
0005 #
0006 
0007 use bytes;
0008 use File::Basename;
0009 
0010 # Default options
0011 $max_width = 79;
0012 
0013 # Clean up space-tab sequences, either by removing spaces or
0014 # replacing them with tabs.
0015 sub clean_space_tabs($)
0016 {
0017     no bytes;           # Tab alignment depends on characters
0018 
0019     my($li) = @_;
0020     my($lo) = '';
0021     my $pos = 0;
0022     my $nsp = 0;
0023     my($i, $c);
0024 
0025     for ($i = 0; $i < length($li); $i++) {
0026     $c = substr($li, $i, 1);
0027     if ($c eq "\t") {
0028         my $npos = ($pos+$nsp+8) & ~7;
0029         my $ntab = ($npos >> 3) - ($pos >> 3);
0030         $lo .= "\t" x $ntab;
0031         $pos = $npos;
0032         $nsp = 0;
0033     } elsif ($c eq "\n" || $c eq "\r") {
0034         $lo .= " " x $nsp;
0035         $pos += $nsp;
0036         $nsp = 0;
0037         $lo .= $c;
0038         $pos = 0;
0039     } elsif ($c eq " ") {
0040         $nsp++;
0041     } else {
0042         $lo .= " " x $nsp;
0043         $pos += $nsp;
0044         $nsp = 0;
0045         $lo .= $c;
0046         $pos++;
0047     }
0048     }
0049     $lo .= " " x $nsp;
0050     return $lo;
0051 }
0052 
0053 # Compute the visual width of a string
0054 sub strwidth($) {
0055     no bytes;           # Tab alignment depends on characters
0056 
0057     my($li) = @_;
0058     my($c, $i);
0059     my $pos = 0;
0060     my $mlen = 0;
0061 
0062     for ($i = 0; $i < length($li); $i++) {
0063     $c = substr($li,$i,1);
0064     if ($c eq "\t") {
0065         $pos = ($pos+8) & ~7;
0066     } elsif ($c eq "\n") {
0067         $mlen = $pos if ($pos > $mlen);
0068         $pos = 0;
0069     } else {
0070         $pos++;
0071     }
0072     }
0073 
0074     $mlen = $pos if ($pos > $mlen);
0075     return $mlen;
0076 }
0077 
0078 $name = basename($0);
0079 
0080 @files = ();
0081 
0082 while (defined($a = shift(@ARGV))) {
0083     if ($a =~ /^-/) {
0084     if ($a eq '-width' || $a eq '-w') {
0085         $max_width = shift(@ARGV)+0;
0086     } else {
0087         print STDERR "Usage: $name [-width #] files...\n";
0088         exit 1;
0089     }
0090     } else {
0091     push(@files, $a);
0092     }
0093 }
0094 
0095 foreach $f ( @files ) {
0096     print STDERR "$name: $f\n";
0097 
0098     if (! -f $f) {
0099     print STDERR "$f: not a file\n";
0100     next;
0101     }
0102 
0103     if (!open(FILE, '+<', $f)) {
0104     print STDERR "$name: Cannot open file: $f: $!\n";
0105     next;
0106     }
0107 
0108     binmode FILE;
0109 
0110     # First, verify that it is not a binary file; consider any file
0111     # with a zero byte to be a binary file.  Is there any better, or
0112     # additional, heuristic that should be applied?
0113     $is_binary = 0;
0114 
0115     while (read(FILE, $data, 65536) > 0) {
0116     if ($data =~ /\0/) {
0117         $is_binary = 1;
0118         last;
0119     }
0120     }
0121 
0122     if ($is_binary) {
0123     print STDERR "$name: $f: binary file\n";
0124     next;
0125     }
0126 
0127     seek(FILE, 0, 0);
0128 
0129     $in_bytes = 0;
0130     $out_bytes = 0;
0131     $lineno = 0;
0132 
0133     @lines  = ();
0134 
0135     $in_hunk = 0;
0136     $err = 0;
0137 
0138     while ( defined($line = <FILE>) ) {
0139     $lineno++;
0140     $in_bytes += length($line);
0141 
0142     if (!$in_hunk) {
0143         if ($line =~
0144         /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@/) {
0145         $minus_lines = $2;
0146         $plus_lines = $4;
0147         if ($minus_lines || $plus_lines) {
0148             $in_hunk = 1;
0149             @hunk_lines = ($line);
0150         }
0151         } else {
0152         push(@lines, $line);
0153         $out_bytes += length($line);
0154         }
0155     } else {
0156         # We're in a hunk
0157 
0158         if ($line =~ /^\+/) {
0159         $plus_lines--;
0160 
0161         $text = substr($line, 1);
0162         $text =~ s/[ \t\r]*$//;     # Remove trailing spaces
0163         $text = clean_space_tabs($text);
0164 
0165         $l_width = strwidth($text);
0166         if ($max_width && $l_width > $max_width) {
0167             print STDERR
0168             "$f:$lineno: adds line exceeds $max_width ",
0169             "characters ($l_width)\n";
0170         }
0171 
0172         push(@hunk_lines, '+'.$text);
0173         } elsif ($line =~ /^\-/) {
0174         $minus_lines--;
0175         push(@hunk_lines, $line);
0176         } elsif ($line =~ /^ /) {
0177         $plus_lines--;
0178         $minus_lines--;
0179         push(@hunk_lines, $line);
0180         } else {
0181         print STDERR "$name: $f: malformed patch\n";
0182         $err = 1;
0183         last;
0184         }
0185 
0186         if ($plus_lines < 0 || $minus_lines < 0) {
0187         print STDERR "$name: $f: malformed patch\n";
0188         $err = 1;
0189         last;
0190         } elsif ($plus_lines == 0 && $minus_lines == 0) {
0191         # End of a hunk.  Process this hunk.
0192         my $i;
0193         my $l;
0194         my @h = ();
0195         my $adj = 0;
0196         my $done = 0;
0197 
0198         for ($i = scalar(@hunk_lines)-1; $i > 0; $i--) {
0199             $l = $hunk_lines[$i];
0200             if (!$done && $l eq "+\n") {
0201             $adj++; # Skip this line
0202             } elsif ($l =~ /^[ +]/) {
0203             $done = 1;
0204             unshift(@h, $l);
0205             } else {
0206             unshift(@h, $l);
0207             }
0208         }
0209 
0210         $l = $hunk_lines[0];  # Hunk header
0211         undef @hunk_lines;    # Free memory
0212 
0213         if ($adj) {
0214             die unless
0215             ($l =~ /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@(.*)$/);
0216             my $mstart = $1;
0217             my $mlin = $2;
0218             my $pstart = $3;
0219             my $plin = $4;
0220             my $tail = $5; # doesn't include the final newline
0221 
0222             $l = sprintf("@@ -%d,%d +%d,%d @@%s\n",
0223                  $mstart, $mlin, $pstart, $plin-$adj,
0224                  $tail);
0225         }
0226         unshift(@h, $l);
0227 
0228         # Transfer to the output array
0229         foreach $l (@h) {
0230             $out_bytes += length($l);
0231             push(@lines, $l);
0232         }
0233 
0234         $in_hunk = 0;
0235         }
0236     }
0237     }
0238 
0239     if ($in_hunk) {
0240     print STDERR "$name: $f: malformed patch\n";
0241     $err = 1;
0242     }
0243 
0244     if (!$err) {
0245     if ($in_bytes != $out_bytes) {
0246         # Only write to the file if changed
0247         seek(FILE, 0, 0);
0248         print FILE @lines;
0249 
0250         if ( !defined($where = tell(FILE)) ||
0251          !truncate(FILE, $where) ) {
0252         die "$name: Failed to truncate modified file: $f: $!\n";
0253         }
0254     }
0255     }
0256 
0257     close(FILE);
0258 }