#!/usr/bin/env perl #- # Copyright (C) 1999-2000 Bradley W. White # Copyright (C) 2000 Rajesh Vaidheeswarran # All rights reserved. # # Redistribution and use are permitted provided that distributions # retain this entire copyright notice and comment. The name of the # author may not be used to endorse or promote products derived # from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. # # $Id: wspace,v 1.3 2000/08/09 18:33:06 rv Exp $ use integer; use strict; =head1 NAME whitespace - Cleanup various types of bogus whitespace in source files. =cut my $prog = $0; $prog =~ s/.*\///; $prog =~ s/\.pl$//; my $cleanup = (@ARGV && $ARGV[0] eq '-c') ? shift : undef; unless (@ARGV) { warn "Usage: $prog [-c] filename ...\n"; exit 1; } =head1 SYNOPSIS B [B<-c>] ... =cut my $ret = 0; foreach my $file (@ARGV) { my $dirty = test(!$cleanup, $file); $ret += ($dirty && $cleanup) ? cleanup($file) : $dirty; } exit $ret; =head1 DESCRIPTION The whitespace program will scan the given files and indicate if the following types of undesirable whitespaces are present. =over 4 =cut sub test { my $verbose = shift; my $file = shift; my $ret = 0; my $writable = 0; return 0 if -d $file; unless (open FILE, $file) { warn "$file: $!\n"; return 0; } if (! -T _) { close FILE; return 0; } $writable = 1 if ( -w _); my $first = 1; my $leading = 0; my $trailing = 0; my $indent = 0; my $spacetab = 0; my $ateol = 0; while () { if (! /^.*\n$/) { warn "$file: Line too long\n"; last; } =item Leading space Empty lines at the top of a file. =cut $leading = 1 if $first && /^[ \t]*$/; =item Trailing space Empty lines at the end of a file. =cut $trailing = /^[ \t]*$/ ? 1 : 0; =item Indentation space 8 or more spaces at the beginning of a line, that should be replaced with TABS. Since this is the most controversial one, here is the rationale: Most terminal drivers and printer drivers have TAB configured or even hardcoded to be 8 spaces. (Some of them allow configuration, but almost always they default to 8.) Changing tab-width to other than 8 and editing will cause your code to look different from within emacs, and say, if you cat it or more it, or even print it. Almost all the popular programming modes let you define an offset (like c-basic-offset or perl-indent-level) to configure the offset, so you should never have to set your tab-width to be other than 8 in all these modes. In fact, with an indent level of say, 4, 2 TABS will cause emacs to replace your 8 spaces with one \t (try it). If vi users in your office complain, tell them to use vim, which distinguishes between tabstop and shiftwidth (vi equivalent of our offsets), and also ask them to set smarttab. That said, if you still don\'t want to test for it, then use the noindent flag. =cut $indent = 1 if /^\s* {8,}/; =item Spaces followed by a TAB. Almost always, we never want that. =cut $spacetab = 1 if / \t/; =item EOL Whitespace Spaces or TABS at the end of a line. =cut $ateol = 1 if /[ \t]$/; $first = 0; } close FILE; ++$ret if $leading; ++$ret if $indent; ++$ret if $spacetab; ++$ret if $ateol; ++$ret if $trailing; if ($verbose) { warn "$file: Leading whitespace\n" if $leading; warn "$file: Indentation whitespace\n" if $indent; warn "$file: Space followed by Tab\n" if $spacetab; warn "$file: End-of-line whitespace\n" if $ateol; warn "$file: Trailing whitespace\n" if $trailing; } return $ret; } =back =head1 OPTIONS =over 10 =item B<-c> Cleanup the detected whitespace and rewrite the file. =cut sub cleanup { my $file = shift; unless (open FILE, $file) { warn "$file: $!\n"; return 0; } my @arr = ; close FILE; # # Leading/Trailing space cleanup # @arr = leadtrailclean(@arr); @arr = reverse leadtrailclean(reverse @arr); # # Indentation cleanup # @arr = indentclean(@arr); # # EOL Space cleanup # @arr = eolclean(@arr); # # Space-followed-by-TAB cleanup # @arr = spctabclean(@arr); my $tmp = $file; $tmp =~ s+/+_+g; $tmp = "/tmp/$tmp.$$"; unless (open FILE, ">$tmp") { warn "$tmp: $!. $file not cleaned\n"; return 0; } print FILE @arr; close FILE; use File::Copy; move($tmp, $file); # # Test the file once again. # return test(1, $file); } sub leadtrailclean { my $first = 1; my @ret = (); foreach (@_) { if ($first) { if (! /^[ \t]*$/) { $first = 0; push @ret, $_; } } else { $first = 0; push @ret, $_; } } return @ret; } sub indentclean { my @ret = (); foreach (@_) { while (/^\s* {8,}/) { $_ =~ s/^(\t*) {8}/$1\t/g; } push @ret, $_; } return @ret; } sub eolclean { my @ret = (); foreach (@_) { $_ =~ s/[ \t]*$//g; push @ret, $_; } return @ret; } sub spctabclean { my @ret = (); foreach (@_) { while (/ \t/) { $_ =~ s/ \t/\t/g; } push @ret, $_; } return @ret; } __END__; =head1 AUTHORS Bradley W. White Rajesh Vaidheeswarran Erv@gnu.orgE =head1 LICENSE Copyright (C) 1999-2000 Bradley W. White Copyright (C) 2000 Rajesh Vaidheeswarran All rights reserved. Redistribution and use are permitted provided that distributions retain this entire copyright notice and comment. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. =cut