1#!/usr/bin/perl
2#***************************************************************************
3#                                  _   _ ____  _
4#  Project                     ___| | | |  _ \| |
5#                             / __| | | | |_) | |
6#                            | (__| |_| |  _ <| |___
7#                             \___|\___/|_| \_\_____|
8#
9# Copyright (C) 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
10#
11# This software is licensed as described in the file COPYING, which
12# you should have received as part of this distribution. The terms
13# are also available at http://curl.haxx.se/docs/copyright.html.
14#
15# You may opt to use, copy, modify, merge, publish, distribute and/or sell
16# copies of the Software, and permit persons to whom the Software is
17# furnished to do so, under the terms of the COPYING file.
18#
19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20# KIND, either express or implied.
21#
22###########################################################################
23
24my $max_column = 79;
25my $indent = 2;
26
27my $warnings;
28my $errors;
29my $file;
30my $dir=".";
31my $wlist;
32
33sub checkwarn {
34    my ($num, $col, $file, $line, $msg, $error) = @_;
35
36    my $w=$error?"error":"warning";
37
38    if($w) {
39        $warnings++;
40    }
41    else {
42        $errors++;
43    }
44
45    $col++;
46    print "$file:$num:$col: $w: $msg\n";
47    print " $line\n";
48
49    if($col < 80) {
50        my $pref = (' ' x $col);
51        print "${pref}^\n";
52    }
53}
54
55$file = shift @ARGV;
56
57while(1) {
58
59    if($file =~ /-D(.*)/) {
60        $dir = $1;
61        $file = shift @ARGV;
62        next;
63    }
64    elsif($file =~ /-W(.*)/) {
65        $wlist = $1;
66        $file = shift @ARGV;
67        next;
68    }
69
70    last;
71}
72
73if(!$file) {
74    print "checksrc.pl [option] <file1> [file2] ...\n";
75    print " Options:\n";
76    print "  -D[DIR]   Directory to prepend file names\n";
77    print "  -W[file]  Whitelist the given file - ignore all its flaws\n";
78    exit;
79}
80
81do {
82    if($file ne "$wlist") {
83        my $fullname = $file;
84        $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/');
85        scanfile($fullname);
86    }
87    $file = shift @ARGV;
88
89} while($file);
90
91
92sub scanfile {
93    my ($file) = @_;
94
95    my $line = 1;
96    my $prevl;
97    my $l;
98    open(R, "<$file") || die "failed to open $file";
99
100    my $copyright=0;
101
102    while(<R>) {
103        chomp;
104        my $l = $_;
105        my $column = 0;
106
107        # check for a copyright statement
108        if(!$copyright && ($l =~ /copyright .* \d\d\d\d/i)) {
109            $copyright=1;
110        }
111
112        # detect long lines
113        if(length($l) > $max_column) {
114            checkwarn($line, length($l), $file, $l, "Longer than $max_column columns");
115        }
116        # detect TAB characters
117        if($l =~ /^(.*)\t/) {
118            checkwarn($line, length($1), $file, $l, "Contains TAB character", 1);
119        }
120        # detect trailing white space
121        if($l =~ /^(.*)[ \t]+\z/) {
122            checkwarn($line, length($1), $file, $l, "Trailing whitespace");
123        }
124
125        # check spaces after for/if/while
126        if($l =~ /^(.*)(for|if|while) \(/) {
127            if($1 =~ / *\#/) {
128                # this is a #if, treat it differently
129            }
130            else {
131                checkwarn($line, length($1)+length($2), $file, $l,
132                          "$2 with space");
133            }
134        }
135
136        # check spaces after open paren after for/if/while
137        if($l =~ /^(.*)(for|if|while)\( /) {
138            if($1 =~ / *\#/) {
139                # this is a #if, treat it differently
140            }
141            else {
142                checkwarn($line, length($1)+length($2)+1, $file, $l,
143                          "$2 with space first in condition");
144            }
145        }
146
147        # check for "} else"
148        if($l =~ /^(.*)\} else/) {
149            checkwarn($line, length($1), $file, $l, "else after closing brace on same line");
150        }
151        # check for open brace first on line but not first column
152        # only alert if previous line ended with a close paren and wasn't a cpp
153        # line
154        if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) {
155            checkwarn($line, length($1), $file, $l, "badly placed open brace");
156        }
157
158        # if the previous line starts with if/while/for AND ends with an open
159        # brace, check that this line is indented $indent more steps, if not
160        # a cpp line
161        if($prevl =~ /^( *)(if|while|for)\(.*\{\z/) {
162            my $first = length($1);
163
164            # this line has some character besides spaces
165            if(($l !~ /^ *#/) && ($l =~ /^( *)[^ ]/)) {
166                my $second = length($1);
167                my $expect = $first+$indent;
168                if($expect != $second) {
169                    my $diff = $second - $first;
170                    checkwarn($line, length($1), $file, $l,
171                              "not indented $indent steps, uses $diff)");
172
173                }
174            }
175        }
176
177        $line++;
178        $prevl = $l;
179    }
180
181    if(!$copyright) {
182        checkwarn(1, 0, $file, "", "Missing copyright statement", 1);
183    }
184
185    close(R);
186
187}
188
189
190if($errors || $warnings) {
191    printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
192    exit 5; # return failure
193}
194