xref: /openbmc/linux/scripts/cleanfile (revision b2441318)
1cb77f0d6SKamil Rytarowski#!/usr/bin/env perl
2b2441318SGreg Kroah-Hartman# SPDX-License-Identifier: GPL-2.0
312b31560SH. Peter Anvin#
412b31560SH. Peter Anvin# Clean a text file -- or directory of text files -- of stealth whitespace.
512b31560SH. Peter Anvin# WARNING: this can be a highly destructive operation.  Use with caution.
612b31560SH. Peter Anvin#
712b31560SH. Peter Anvin
8cb77f0d6SKamil Rytarowskiuse warnings;
912b31560SH. Peter Anvinuse bytes;
1012b31560SH. Peter Anvinuse File::Basename;
1112b31560SH. Peter Anvin
12cb3ed5b7SH. Peter Anvin# Default options
13cb3ed5b7SH. Peter Anvin$max_width = 79;
14cb3ed5b7SH. Peter Anvin
1512b31560SH. Peter Anvin# Clean up space-tab sequences, either by removing spaces or
1612b31560SH. Peter Anvin# replacing them with tabs.
1712b31560SH. Peter Anvinsub clean_space_tabs($)
1812b31560SH. Peter Anvin{
1912b31560SH. Peter Anvin    no bytes;			# Tab alignment depends on characters
2012b31560SH. Peter Anvin
2112b31560SH. Peter Anvin    my($li) = @_;
2212b31560SH. Peter Anvin    my($lo) = '';
2312b31560SH. Peter Anvin    my $pos = 0;
2412b31560SH. Peter Anvin    my $nsp = 0;
2512b31560SH. Peter Anvin    my($i, $c);
2612b31560SH. Peter Anvin
2712b31560SH. Peter Anvin    for ($i = 0; $i < length($li); $i++) {
2812b31560SH. Peter Anvin	$c = substr($li, $i, 1);
2912b31560SH. Peter Anvin	if ($c eq "\t") {
3012b31560SH. Peter Anvin	    my $npos = ($pos+$nsp+8) & ~7;
3112b31560SH. Peter Anvin	    my $ntab = ($npos >> 3) - ($pos >> 3);
3212b31560SH. Peter Anvin	    $lo .= "\t" x $ntab;
3312b31560SH. Peter Anvin	    $pos = $npos;
3412b31560SH. Peter Anvin	    $nsp = 0;
3512b31560SH. Peter Anvin	} elsif ($c eq "\n" || $c eq "\r") {
3612b31560SH. Peter Anvin	    $lo .= " " x $nsp;
3712b31560SH. Peter Anvin	    $pos += $nsp;
3812b31560SH. Peter Anvin	    $nsp = 0;
3912b31560SH. Peter Anvin	    $lo .= $c;
4012b31560SH. Peter Anvin	    $pos = 0;
4112b31560SH. Peter Anvin	} elsif ($c eq " ") {
4212b31560SH. Peter Anvin	    $nsp++;
4312b31560SH. Peter Anvin	} else {
4412b31560SH. Peter Anvin	    $lo .= " " x $nsp;
4512b31560SH. Peter Anvin	    $pos += $nsp;
4612b31560SH. Peter Anvin	    $nsp = 0;
4712b31560SH. Peter Anvin	    $lo .= $c;
4812b31560SH. Peter Anvin	    $pos++;
4912b31560SH. Peter Anvin	}
5012b31560SH. Peter Anvin    }
5112b31560SH. Peter Anvin    $lo .= " " x $nsp;
5212b31560SH. Peter Anvin    return $lo;
5312b31560SH. Peter Anvin}
5412b31560SH. 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
8012b31560SH. Peter Anvin$name = basename($0);
8112b31560SH. 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 ) {
9812b31560SH. Peter Anvin    print STDERR "$name: $f\n";
9912b31560SH. Peter Anvin
10012b31560SH. Peter Anvin    if (! -f $f) {
10112b31560SH. Peter Anvin	print STDERR "$f: not a file\n";
10212b31560SH. Peter Anvin	next;
10312b31560SH. Peter Anvin    }
10412b31560SH. Peter Anvin
10512b31560SH. Peter Anvin    if (!open(FILE, '+<', $f)) {
10612b31560SH. Peter Anvin	print STDERR "$name: Cannot open file: $f: $!\n";
10712b31560SH. Peter Anvin	next;
10812b31560SH. Peter Anvin    }
10912b31560SH. Peter Anvin
11012b31560SH. Peter Anvin    binmode FILE;
11112b31560SH. Peter Anvin
11212b31560SH. Peter Anvin    # First, verify that it is not a binary file; consider any file
11312b31560SH. Peter Anvin    # with a zero byte to be a binary file.  Is there any better, or
11412b31560SH. Peter Anvin    # additional, heuristic that should be applied?
11512b31560SH. Peter Anvin    $is_binary = 0;
11612b31560SH. Peter Anvin
11712b31560SH. Peter Anvin    while (read(FILE, $data, 65536) > 0) {
11812b31560SH. Peter Anvin	if ($data =~ /\0/) {
11912b31560SH. Peter Anvin	    $is_binary = 1;
12012b31560SH. Peter Anvin	    last;
12112b31560SH. Peter Anvin	}
12212b31560SH. Peter Anvin    }
12312b31560SH. Peter Anvin
12412b31560SH. Peter Anvin    if ($is_binary) {
12512b31560SH. Peter Anvin	print STDERR "$name: $f: binary file\n";
12612b31560SH. Peter Anvin	next;
12712b31560SH. Peter Anvin    }
12812b31560SH. Peter Anvin
12912b31560SH. Peter Anvin    seek(FILE, 0, 0);
13012b31560SH. Peter Anvin
13112b31560SH. Peter Anvin    $in_bytes = 0;
13212b31560SH. Peter Anvin    $out_bytes = 0;
13312b31560SH. Peter Anvin    $blank_bytes = 0;
13412b31560SH. Peter Anvin
13512b31560SH. Peter Anvin    @blanks = ();
13612b31560SH. Peter Anvin    @lines  = ();
137cb3ed5b7SH. Peter Anvin    $lineno = 0;
13812b31560SH. Peter Anvin
13912b31560SH. Peter Anvin    while ( defined($line = <FILE>) ) {
140cb3ed5b7SH. Peter Anvin	$lineno++;
14112b31560SH. Peter Anvin	$in_bytes += length($line);
14212b31560SH. Peter Anvin	$line =~ s/[ \t\r]*$//;		# Remove trailing spaces
14312b31560SH. Peter Anvin	$line = clean_space_tabs($line);
14412b31560SH. Peter Anvin
14512b31560SH. Peter Anvin	if ( $line eq "\n" ) {
14612b31560SH. Peter Anvin	    push(@blanks, $line);
14712b31560SH. Peter Anvin	    $blank_bytes += length($line);
14812b31560SH. Peter Anvin	} else {
14912b31560SH. Peter Anvin	    push(@lines, @blanks);
15012b31560SH. Peter Anvin	    $out_bytes += $blank_bytes;
15112b31560SH. Peter Anvin	    push(@lines, $line);
15212b31560SH. Peter Anvin	    $out_bytes += length($line);
15312b31560SH. Peter Anvin	    @blanks = ();
15412b31560SH. Peter Anvin	    $blank_bytes = 0;
15512b31560SH. Peter Anvin	}
156cb3ed5b7SH. Peter Anvin
157cb3ed5b7SH. Peter Anvin	$l_width = strwidth($line);
158cb3ed5b7SH. Peter Anvin	if ($max_width && $l_width > $max_width) {
159cb3ed5b7SH. Peter Anvin	    print STDERR
160cb3ed5b7SH. Peter Anvin		"$f:$lineno: line exceeds $max_width characters ($l_width)\n";
161cb3ed5b7SH. Peter Anvin	}
16212b31560SH. Peter Anvin    }
16312b31560SH. Peter Anvin
16412b31560SH. Peter Anvin    # Any blanks at the end of the file are discarded
16512b31560SH. Peter Anvin
16612b31560SH. Peter Anvin    if ($in_bytes != $out_bytes) {
16712b31560SH. Peter Anvin	# Only write to the file if changed
16812b31560SH. Peter Anvin	seek(FILE, 0, 0);
16912b31560SH. Peter Anvin	print FILE @lines;
17012b31560SH. Peter Anvin
17112b31560SH. Peter Anvin	if ( !defined($where = tell(FILE)) ||
17212b31560SH. Peter Anvin	     !truncate(FILE, $where) ) {
17312b31560SH. Peter Anvin	    die "$name: Failed to truncate modified file: $f: $!\n";
17412b31560SH. Peter Anvin	}
17512b31560SH. Peter Anvin    }
17612b31560SH. Peter Anvin
17712b31560SH. Peter Anvin    close(FILE);
17812b31560SH. Peter Anvin}
179