• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/perl
2#***************************************************************************
3#                                  _   _ ____  _
4#  Project                     ___| | | |  _ \| |
5#                             / __| | | | |_) | |
6#                            | (__| |_| |  _ <| |___
7#                             \___|\___/|_| \_\_____|
8#
9# Copyright (C) 2011 - 2015, 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 $supressed; # whitelisted problems
30my $file;
31my $dir=".";
32my $wlist;
33my $windows_os = $^O eq 'MSWin32' || $^O eq 'msys' || $^O eq 'cygwin';
34
35my %whitelist;
36
37sub readwhitelist {
38    open(W, "<$dir/checksrc.whitelist");
39    my @all=<W>;
40    for(@all) {
41        $windows_os ? $_ =~ s/\r?\n$// : chomp;
42        $whitelist{$_}=1;
43    }
44    close(W);
45}
46
47sub checkwarn {
48    my ($num, $col, $file, $line, $msg, $error) = @_;
49
50    if($whitelist{$line}) {
51        $supressed++;
52        return;
53    }
54
55    my $w=$error?"error":"warning";
56
57    if($w) {
58        $warnings++;
59    }
60    else {
61        $errors++;
62    }
63
64    $col++;
65    print "$file:$num:$col: $w: $msg\n";
66    print " $line\n";
67
68    if($col < 80) {
69        my $pref = (' ' x $col);
70        print "${pref}^\n";
71    }
72}
73
74$file = shift @ARGV;
75
76while(1) {
77
78    if($file =~ /-D(.*)/) {
79        $dir = $1;
80        $file = shift @ARGV;
81        next;
82    }
83    elsif($file =~ /-W(.*)/) {
84        $wlist .= " $1 ";
85        $file = shift @ARGV;
86        next;
87    }
88
89    last;
90}
91
92if(!$file) {
93    print "checksrc.pl [option] <file1> [file2] ...\n";
94    print " Options:\n";
95    print "  -D[DIR]   Directory to prepend file names\n";
96    print "  -W[file]  Whitelist the given file - ignore all its flaws\n";
97    exit;
98}
99
100readwhitelist();
101
102do {
103    if("$wlist" !~ / $file /) {
104        my $fullname = $file;
105        $fullname = "$dir/$file" if ($fullname !~ '^\.?\.?/');
106        scanfile($fullname);
107    }
108    $file = shift @ARGV;
109
110} while($file);
111
112
113sub scanfile {
114    my ($file) = @_;
115
116    my $line = 1;
117    my $prevl;
118    my $l;
119    open(R, "<$file") || die "failed to open $file";
120
121    my $copyright=0;
122
123    while(<R>) {
124        $windows_os ? $_ =~ s/\r?\n$// : chomp;
125        my $l = $_;
126        my $column = 0;
127
128        # check for a copyright statement
129        if(!$copyright && ($l =~ /copyright .* \d\d\d\d/i)) {
130            $copyright=1;
131        }
132
133        # detect long lines
134        if(length($l) > $max_column) {
135            checkwarn($line, length($l), $file, $l, "Longer than $max_column columns");
136        }
137        # detect TAB characters
138        if($l =~ /^(.*)\t/) {
139            checkwarn($line, length($1), $file, $l, "Contains TAB character", 1);
140        }
141        # detect trailing white space
142        if($l =~ /^(.*)[ \t]+\z/) {
143            checkwarn($line, length($1), $file, $l, "Trailing whitespace");
144        }
145
146        # check spaces after for/if/while
147        if($l =~ /^(.*)(for|if|while) \(/) {
148            if($1 =~ / *\#/) {
149                # this is a #if, treat it differently
150            }
151            else {
152                checkwarn($line, length($1)+length($2), $file, $l,
153                          "$2 with space");
154            }
155        }
156
157        # check spaces after open paren after for/if/while
158        if($l =~ /^(.*)(for|if|while)\( /) {
159            if($1 =~ / *\#/) {
160                # this is a #if, treat it differently
161            }
162            else {
163                checkwarn($line, length($1)+length($2)+1, $file, $l,
164                          "$2 with space first in condition");
165            }
166        }
167
168        # check for "return(" without space
169        if($l =~ /^(.*)return\(/) {
170            if($1 =~ / *\#/) {
171                # this is a #if, treat it differently
172            }
173            else {
174                checkwarn($line, length($1)+6, $file, $l,
175                          "return without space before paren");
176            }
177        }
178
179        # check for comma without space
180        if($l =~ /^(.*),[^ \n]/) {
181            my $pref=$1;
182            my $ign=0;
183            if($pref =~ / *\#/) {
184                # this is a #if, treat it differently
185                $ign=1;
186            }
187            elsif($pref =~ /\/\*/) {
188                # this is a comment
189                $ign=1;
190            }
191            elsif($pref =~ /[\"\']/) {
192                $ign = 1;
193                # There is a quote here, figure out whether the comma is
194                # within a string or '' or not.
195                if($pref =~ /\"/) {
196                    # withing a string
197                }
198                elsif($pref =~ /\'$/) {
199                    # a single letter
200                }
201                else {
202                    $ign = 0;
203                }
204            }
205            if(!$ign) {
206                checkwarn($line, length($pref)+1, $file, $l,
207                          "comma without following space");
208            }
209        }
210
211        # check for "} else"
212        if($l =~ /^(.*)\} *else/) {
213            checkwarn($line, length($1), $file, $l, "else after closing brace on same line");
214        }
215        # check for "){"
216        if($l =~ /^(.*)\)\{/) {
217            checkwarn($line, length($1)+1, $file, $l, "missing space after close paren");
218        }
219
220        # check for space before the semicolon last in a line
221        if($l =~ /^(.*[^ ].*) ;$/) {
222            checkwarn($line, length($1), $file, $l, "space before last semicolon");
223        }
224
225        # scan for use of banned functions
226        if($l =~ /^(.*\W)(sprintf|vsprintf|strcat|strncat|gets)\s*\(/) {
227            checkwarn($line, length($1), $file, $l,
228                      "use of $2 is banned");
229        }
230
231        # scan for use of non-binary fopen without the macro
232        if($l =~ /^(.*\W)fopen\s*\([^"]*\"([^"]*)/) {
233            my $mode = $2;
234            if($mode !~ /b/) {
235                checkwarn($line, length($1), $file, $l,
236                          "use of non-binary fopen without FOPEN_* macro");
237            }
238        }
239
240        # check for open brace first on line but not first column
241        # only alert if previous line ended with a close paren and wasn't a cpp
242        # line
243        if((($prevl =~ /\)\z/) && ($prevl !~ /^ *#/)) && ($l =~ /^( +)\{/)) {
244            checkwarn($line, length($1), $file, $l, "badly placed open brace");
245        }
246
247        # if the previous line starts with if/while/for AND ends with an open
248        # brace, check that this line is indented $indent more steps, if not
249        # a cpp line
250        if($prevl =~ /^( *)(if|while|for)\(.*\{\z/) {
251            my $first = length($1);
252
253            # this line has some character besides spaces
254            if(($l !~ /^ *#/) && ($l =~ /^( *)[^ ]/)) {
255                my $second = length($1);
256                my $expect = $first+$indent;
257                if($expect != $second) {
258                    my $diff = $second - $first;
259                    checkwarn($line, length($1), $file, $l,
260                              "not indented $indent steps, uses $diff)");
261
262                }
263            }
264        }
265
266        $line++;
267        $prevl = $l;
268    }
269
270    if(!$copyright) {
271        checkwarn(1, 0, $file, "", "Missing copyright statement", 1);
272    }
273
274    close(R);
275
276}
277
278
279if($errors || $warnings) {
280    printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
281    exit 5; # return failure
282}
283