use strict;
use warnings;
use File::Basename;
sub srcline {
my ($state, $flags, $l) = @_;
my $line = "";
if(($state == 0) && ($l =~ /^ *\# *include/)) {
$flags |= 1;
}
else {
$flags &= ~1;
}
if($state == 3) {
$state = 0;
}
my @c = split(//, $l);
for my $c (@c) {
if($state == 1) {
if($c eq "/") {
$line .= "//";
$state = 3;
}
elsif($c eq "*") {
$state = 4;
$line .= "/*";
}
else {
$line .= " ";
$state = 0;
}
}
elsif($state == 2) {
if($c eq "\\") {
$line .= "\\";
$state = 7;
}
elsif($c eq "\"") {
$line .= "\"";
$state = 0;
}
else {
$line .= $c;
}
}
elsif($state == 3) {
$line .= $c;
}
elsif($state == 4) {
if($c eq "*") {
$state = 5;
}
else {
$line .= $c;
}
}
elsif($state == 5) {
if($c eq "/") {
$line .= "*/";
$state = 0;
}
else {
$line .= "*$c";
$state = 4;
}
}
elsif($state == 7) {
$line .= $c;
$state = 2;
}
else {
if($c eq "/") {
$state = 1; }
elsif(($c eq "\"") && !($flags & 1)) {
$line .= "\"";
$state = 2;
}
elsif($c eq "\n") {
$line .= "\n";
}
else {
$line .= " ";
}
}
}
return $state, $flags, $line;
}
sub sourcecode {
my ($f) = @_;
my $state = 0;
my $flags = 0;
my @lines;
my $line;
open(F, "<$f");
while(<F>) {
my $l = $_;
($state, $flags, $line) = srcline($state, $flags, $l);
push @lines, $line;
}
close(F);
return @lines;
}
my @whitelist = (
'(^|\W)((https|http|ftp):\/\/[a-z0-9\-._~%:\/?\#\[\]\@!\$&\'\(\)*+,;=]+)',
'\*\*.*?\*\*',
'\`.*?\`'
);
my %alt;
my %exactcase;
my %wl;
my @w;
my @exact;
my $file = shift @ARGV;
open(CONFIG, "<$file") or die "Cannot open '$file': $!";
while(<CONFIG>) {
chomp;
if($_ =~ /^#/) {
next;
}
if(/^---:([^:]*):(.*)/) {
my $word = lc($2);
$wl{"$1:$word"} = 1;
}
elsif($_ =~ /^---(.+)/) {
push @whitelist, $1;
}
elsif($_ =~ /^(.*)([:=])(.*)/) {
my ($bad, $sep, $better) = ($1, $2, $3);
if($sep eq "=") {
$alt{$bad} = $better;
push @exact, $bad;
}
else {
$alt{lc($bad)} = $better;
push @w, $bad;
}
}
}
close(CONFIG);
my $re_ci;
if(@w) {
my $pat = join('|', map { quotemeta($_) } @w);
$re_ci = qr/\b($pat)\b/i;
}
my $re_cs;
if(@exact) {
my $pat = join('|', map { quotemeta($_) } @exact);
$re_cs = qr/\b($pat)\b/;
}
my $pat = join('|', map { $_ } @whitelist);
my $re_wl = qr/($pat)/;
my $errors = 0;
sub highlight {
my ($p, $w, $in, $f, $l, $lookup) = @_;
my $c = length($p)+1;
my $ch;
my $dir = dirname($f);
$ch = $dir . "/" . ":" . lc($w);
if($wl{$ch}) {
return;
}
my $updir = dirname($dir);
if($dir ne $updir) {
$ch = $updir . "/" . ":" . lc($w);
if($wl{$ch}) {
return;
}
}
$ch = $f . ":" . lc($w);
if($wl{$ch}) {
return;
}
print STDERR "$f:$l:$c: error: found bad word \"$w\"\n";
printf STDERR " %4d | %s\n", $l, $in;
printf STDERR " | %*s^%s\n", length($p), " ",
"~" x (length($w)-1);
printf STDERR " maybe use \"%s\" instead?\n", $alt{$lookup};
$errors++;
}
sub document {
my ($f) = @_;
my @lines;
open(F, "<$f");
while(<F>) {
push @lines, $_;
}
close(F);
return @lines;
}
sub file {
my ($f) = @_;
my $l = 0;
my $skip_indented = 0;
my $source_code = 0;
if($f =~ /\.[ch]$/) {
$source_code = 1;
}
else {
$skip_indented = 1;
}
my @lines;
if($source_code) {
@lines = sourcecode($f);
}
else {
@lines = document($f);
}
for my $in (@lines) {
$l++;
chomp $in;
if($skip_indented && $in =~ /^ /) {
next;
}
$in =~ s/(\[.*\])\(.*\)/$1/g;
if($re_wl) {
$in =~ s/${re_wl}//ig;
}
if($re_ci) {
if($in =~ /^(.*)$re_ci/i) {
highlight($1, $2, $in, $f, $l, lc($2));
}
}
if($re_cs) {
if($in =~ /^(.*)$re_cs/) {
highlight($1, $2, $in, $f, $l, $2);
}
}
}
}
my @filemasks = @ARGV;
open(my $git_ls_files, '-|', 'git', 'ls-files', '--', ":!:$file", @filemasks) or die "Failed running git ls-files: $!";
my @files;
while(my $each = <$git_ls_files>) {
chomp $each;
push @files, $each;
}
close $git_ls_files;
my $onum = scalar(@files);
my $num;
for my $e (@files) {
file($e);
}
exit $errors;