#!/usr/bin/perl use strict; use warnings; use File::Basename; # Desired indentation. my $indentation = 2; if ($#ARGV != 0) { print "\nUsage: stylecheck.pl source\n"; exit; } my $source = $ARGV[0]; open(my $in, "<", $source) or die "Can't open source: $!"; my $lineno = 0; my @c_source = <$in>; my $filename = $source; $filename =~ y/\\/\//; $filename = basename($filename); my $cr = "\r"; my $lf = "\n"; my $tab = "\t"; sub style { my $desc = shift; print("style: $desc at line $lineno in \"$source\"\n"); } sub error { my $desc = shift; print("error: $desc at line $lineno in \"$source\"\n"); } my $emptycnt = 0; my $c_comment_complete = 0; my $c_comment = ""; my $state = "start"; foreach my $line (@c_source) { $lineno += 1; #**************************************************************************** # Processing comments after decoding. if ($c_comment_complete != 0) { # print($c_comment . "\n"); #****************************************************************************** # Special case of lint comment. if ("$c_comment" =~ /^\s*\/\*lint/) { } else { #****************************************************************************** # Check on glued doxygen back-comment start. if ("$c_comment" =~ /^\s*\/\*\*<[^\s]/) { style "detected glued doxygen back-comment start"; } #****************************************************************************** # Check on glued doxygen comment start. if ("$c_comment" =~ /^\s*\/\*\*[^\s<]/) { style "detected glued doxygen comment start"; } #****************************************************************************** # Check on glued comment start. if ("$c_comment" =~ /^\s*\/\*[^\s\*=]/) { style "detected glued comment start"; } #****************************************************************************** # Check on lower case letter at comment beginning. if ("$c_comment" =~ /^\s*\/\*\s*[a-z]/) { style "detected lower case comment start"; } #****************************************************************************** # Check on loose comment stop. # if ("$line" =~ /\s\*\//) { # style "detected loose comment stop"; # } } $c_comment_complete = 0; } #**************************************************************************** # Check on EOL. if (not ($line =~ /$cr$lf$/)) { error "detected malformed EOL"; } $line =~ s/$cr//; $line =~ s/$lf//; #**************************************************************************** # Check on trailing spaces. if ($line =~ /\s$/) { style "detected trailing spaces"; } #**************************************************************************** # Check on TABs. if ($line =~ /$tab/) { style "detected TAB"; $line =~ s/$tab/ /; } #**************************************************************************** # Check on empty lines. my $tmp = $line; $tmp =~ s/\s//; if (length($tmp) == 0) { $emptycnt = $emptycnt + 1; if ($emptycnt == 2) { style "detected multiple empty lines" } next; } else { $emptycnt = 0; } #**************************************************************************** # Stripping strings content for ease of parsing, all strings become _string_. $line =~ s/\\\"//; if ($line =~ s/(\"[^"]*\")/_string_/) { # print "string: $1 replaced by _string_\n"; } #****************************************************************************** # State machine handling. if ($state eq "start") { #****************************************************************************** # Standard separator. #****************************************************************************** # Comment start matching. if ("$line" =~ /\/\*/) { #****************************************************************************** # Single or multi line comments. if ("$line" =~ /\*\//) { # Special case of single line comments. $line =~ /(\/\*.*\*\/)/; $c_comment = $1; $c_comment_complete = 1; } else { # Start of multi-line comment. $line =~ /(\/\*.*)/; $c_comment = $1; $state = "incomment"; } } else { #**************************************************************************** # Check on C++ comments. if ($line =~ /\/\//) { style "detected // comment"; } #**************************************************************************** # Check on commas. if ($line =~ /,\S/) { style "detected comma not followed by space"; } #**************************************************************************** # Check on loose semicolons. if ($line =~ /\S\s;/) { style "detected loose semicolon"; } #**************************************************************************** # Check on glued keywords. if ($line =~ /\sif\(/) { style "detected glued \"if\""; } if ($line =~ /\sfor\(/) { style "detected glued \"for\""; } if ($line =~ /\swhile\(/) { style "detected glued \"while\""; } if ($line =~ /\)while/) { style "detected glued \"while\""; } if ($line =~ /\sswitch\(/) { style "detected glued \"switch\""; } if ($line =~ /\sdo\{/) { style "detected glued \"do\""; } #**************************************************************************** # Check on loose parenthesis. if ($line =~ /\(\s+/) { style "detected loose \"(\""; } if ($line =~ /\S\s+\)/) { style "detected loose \")\""; } #**************************************************************************** # Check on glued braces. if ($line =~ /\)\{/) { style "detected glued left brace"; } if ($line =~ /\w\{/) { style "detected glued left brace"; } if ($line =~ /\}\w/) { style "detected glued right brace"; } #**************************************************************************** # Check on (some) operators. # Before: <<= << >>= >> <= >= == != += -= *= /= %= &= |= ^= if ($line =~ /(\(\S<<=?|\S>>=?|[^\s<]<=|[^\s>]>=|\S[=!+\-*\/%&|^]=)/) { style "detected glued operator (1)"; } # After: = elsif ($line =~ /=[^\s=]/) { style "detected glued assignment/comparison operator (2)"; } # Before: = elsif ($line =~ /[^\s\=\!\+\-\*\/\%\&\|\^\<\>]=/) { style "detected glued assignment/comparison operator (3)"; } # After: << >> elsif ($line =~ /(<<|>>)[^\s=]/) { style "detected glued assignment/comparison operator (4)"; } # Before: && || ^^ elsif ($line =~ /\S(&&|\|\||\^\^)/) { style "detected glued logical operator (1)"; } # After: && || ^^ elsif ($line =~ /(&&|\|\||\^\^)\S/) { style "detected glued logical operator (2)"; } #**************************************************************************** # Check function-call-like returns (not perfect so disabled). if ($line =~ /return\s*\(/) { if ($line =~ /return\s*\([\w\d\s\*]*\)\s*[^;]/) { } else { # style "detected function-call-like return"; } } } } #****************************************************************************** # Scanning for comment end. elsif ($state eq "incomment") { # Left trimming. $line =~ s/^\s+//; if ("$line" =~ /^\s*\*\/\s*$/) { # Just end of comment line. $c_comment .= "*/"; $c_comment_complete = 1; # print("$c_comment"); $state = "start"; } elsif ("$line" =~ /\*\/\s*$/) { # Text followed by end of comment. $line =~ /(.*\*\/)/; $c_comment .= " " . $1; $c_comment_complete = 1; # print("$c_comment"); $state = "start"; } else { # Add the whole line, remove first * and following spaces if any. $line =~ s/^\*?\s*//; $c_comment .= " " . $line; # print("$c_comment"); } } } close $in or die "$in: $!";