Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/scripts/checksrc.pl
2649 views
1
#!/usr/bin/env perl
2
#***************************************************************************
3
# _ _ ____ _
4
# Project ___| | | | _ \| |
5
# / __| | | | |_) | |
6
# | (__| |_| | _ <| |___
7
# \___|\___/|_| \_\_____|
8
#
9
# Copyright (C) Daniel Stenberg, <[email protected]>, 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 https://curl.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
# SPDX-License-Identifier: curl
23
#
24
###########################################################################
25
26
use strict;
27
use warnings;
28
29
my $max_column = 79;
30
my $indent = 2;
31
32
my $warnings = 0;
33
my $swarnings = 0;
34
my $errors = 0;
35
my $serrors = 0;
36
my $suppressed; # skipped problems
37
my $file;
38
my $dir=".";
39
my $wlist="";
40
my @alist;
41
my $windows_os = $^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys';
42
my $verbose = 0;
43
my %skiplist;
44
45
my %ignore;
46
my %ignore_set;
47
my %ignore_used;
48
my @ignore_line;
49
50
my %banfunc = (
51
"gmtime" => 1,
52
"localtime" => 1,
53
"gets" => 1,
54
"strtok" => 1,
55
"sprintf" => 1,
56
"snprintf" => 1,
57
"vsprintf" => 1,
58
"vsnprintf" => 1,
59
"aprintf" => 1,
60
"fprintf" => 1,
61
"msnprintf" => 1,
62
"mvsnprintf" => 1,
63
"printf" => 1,
64
"vaprintf" => 1,
65
"vfprintf" => 1,
66
"vprintf" => 1,
67
"sscanf" => 1,
68
"strcat" => 1,
69
"strerror" => 1,
70
"strncat" => 1,
71
"strncpy" => 1,
72
"strtok_r" => 1,
73
"strtol" => 1,
74
"strtoul" => 1,
75
"_mbscat" => 1,
76
"_mbsncat" => 1,
77
"_tcscat" => 1,
78
"_tcsdup" => 1,
79
"_tcsncat" => 1,
80
"_wcscat" => 1,
81
"_wcsncat" => 1,
82
"_wcsdup" => 1,
83
"wcsdup" => 1,
84
"LoadLibrary" => 1,
85
"LoadLibraryA" => 1,
86
"LoadLibraryW" => 1,
87
"LoadLibraryEx" => 1,
88
"LoadLibraryExA" => 1,
89
"LoadLibraryExW" => 1,
90
"WSASocket" => 1,
91
"WSASocketA" => 1,
92
"WSASocketW" => 1,
93
"_waccess" => 1,
94
"_access" => 1,
95
"access" => 1,
96
"accept" => 1,
97
"accept4" => 1,
98
"freeaddrinfo" => 1,
99
"getaddrinfo" => 1,
100
"recv" => 1,
101
"send" => 1,
102
"socket" => 1,
103
"socketpair" => 1,
104
"fclose" => 1,
105
"fdopen" => 1,
106
"fopen" => 1,
107
"open" => 1,
108
"stat" => 1,
109
);
110
111
my %warnings_extended = (
112
'COPYRIGHTYEAR' => 'copyright year incorrect',
113
'STDERR', => 'stderr detected',
114
);
115
116
my %warnings = (
117
'ASSIGNWITHINCONDITION' => 'assignment within conditional expression',
118
'ASTERISKNOSPACE' => 'pointer declared without space before asterisk',
119
'ASTERISKSPACE' => 'pointer declared with space after asterisk',
120
'BADCOMMAND' => 'bad !checksrc! instruction',
121
'BANNEDFUNC' => 'a banned function was used',
122
'BANNEDPREPROC' => 'a banned symbol was used on a preprocessor line',
123
'BRACEELSE' => '} else on the same line',
124
'BRACEPOS' => 'wrong position for an open brace',
125
'BRACEWHILE' => 'A single space between open brace and while',
126
'COMMANOSPACE' => 'comma without following space',
127
'COMMENTNOSPACEEND' => 'no space before */',
128
'COMMENTNOSPACESTART' => 'no space following /*',
129
'COPYRIGHT' => 'file missing a copyright statement',
130
'CPPCOMMENTS' => '// comment detected',
131
'DOBRACE' => 'A single space between do and open brace',
132
'EMPTYLINEBRACE' => 'Empty line before the open brace',
133
'EQUALSNOSPACE' => 'equals sign without following space',
134
'EQUALSNULL' => 'if/while comparison with == NULL',
135
'ERRNOVAR' => 'use of bare errno define',
136
'EXCLAMATIONSPACE' => 'Whitespace after exclamation mark in expression',
137
'FIXME' => 'FIXME or TODO comment',
138
'FOPENMODE' => 'fopen needs a macro for the mode string',
139
'INCLUDEDUP', => 'same file is included again',
140
'INDENTATION' => 'wrong start column for code',
141
'LONGLINE' => "Line longer than $max_column",
142
'SPACEBEFORELABEL' => 'labels not at the start of the line',
143
'MULTISPACE' => 'multiple spaces used when not suitable',
144
'NOSPACEAND' => 'missing space around Logical AND operator',
145
'NOSPACEC' => 'missing space around ternary colon operator',
146
'NOSPACEEQUALS' => 'equals sign without preceding space',
147
'NOSPACEQ' => 'missing space around ternary question mark operator',
148
'NOSPACETHAN' => 'missing space around less or greater than',
149
'NOTEQUALSZERO', => 'if/while comparison with != 0',
150
'ONELINECONDITION' => 'conditional block on the same line as the if()',
151
'OPENCOMMENT' => 'file ended with a /* comment still "open"',
152
'PARENBRACE' => '){ without sufficient space',
153
'RETURNNOSPACE' => 'return without space',
154
'SEMINOSPACE' => 'semicolon without following space',
155
'SIZEOFNOPAREN' => 'use of sizeof without parentheses',
156
'SPACEAFTERPAREN' => 'space after open parenthesis',
157
'SPACEBEFORECLOSE' => 'space before a close parenthesis',
158
'SPACEBEFORECOMMA' => 'space before a comma',
159
'SPACEBEFOREPAREN' => 'space before an open parenthesis',
160
'SPACESEMICOLON' => 'space before semicolon',
161
'SPACESWITCHCOLON' => 'space before colon of switch label',
162
'TABS' => 'TAB characters not allowed',
163
'TRAILINGSPACE' => 'Trailing whitespace on the line',
164
'TYPEDEFSTRUCT' => 'typedefed struct',
165
'UNUSEDIGNORE' => 'a warning ignore was not used',
166
);
167
168
sub readskiplist {
169
open(my $W, '<', "$dir/checksrc.skip") or return;
170
my @all=<$W>;
171
for(@all) {
172
$windows_os ? $_ =~ s/\r?\n$// : chomp;
173
$skiplist{$_}=1;
174
}
175
close($W);
176
}
177
178
# Reads the .checksrc in $dir for any extended warnings to enable locally.
179
# Currently there is no support for disabling warnings from the standard set,
180
# and since that's already handled via !checksrc! commands there is probably
181
# little use to add it.
182
sub readlocalfile {
183
my ($file) = @_;
184
my $i = 0;
185
my $rcfile;
186
187
if(($dir eq ".") && $file =~ /\//) {
188
my $ldir;
189
if($file =~ /(.*)\//) {
190
$ldir = $1;
191
open($rcfile, "<", "$dir/$ldir/.checksrc") or return;
192
}
193
}
194
else {
195
open($rcfile, "<", "$dir/.checksrc") or return;
196
}
197
198
while(<$rcfile>) {
199
$windows_os ? $_ =~ s/\r?\n$// : chomp;
200
$i++;
201
202
# Lines starting with '#' are considered comments
203
if(/^\s*(#.*)/) {
204
next;
205
}
206
# Skip empty lines
207
elsif($_ eq '') {
208
next;
209
}
210
elsif(/^enable ([A-Z]+)$/) {
211
if(!defined($warnings_extended{$1})) {
212
print STDERR "invalid warning specified in .checksrc: \"$1\"\n";
213
next;
214
}
215
$warnings{$1} = $warnings_extended{$1};
216
}
217
elsif(/^disable ([A-Z]+)$/) {
218
if(!defined($warnings{$1})) {
219
print STDERR "invalid warning specified in .checksrc: \"$1\"\n";
220
next;
221
}
222
# Accept-list
223
push @alist, $1;
224
}
225
elsif(/^banfunc ([^ ]*)/) {
226
$banfunc{$1} = $1;
227
}
228
elsif(/^allowfunc ([^ ]*)/) {
229
undef $banfunc{$1};
230
}
231
else {
232
die "Invalid format in $dir/.checksrc on line $i: $_\n";
233
}
234
}
235
close($rcfile);
236
}
237
238
sub checkwarn {
239
my ($name, $num, $col, $file, $line, $msg, $error) = @_;
240
241
my $w=$error?"error":"warning";
242
my $nowarn=0;
243
244
#if(!$warnings{$name}) {
245
# print STDERR "Dev! there's no description for $name!\n";
246
#}
247
248
# checksrc.skip
249
if($skiplist{$line}) {
250
$nowarn = 1;
251
}
252
# !checksrc! controlled
253
elsif($ignore{$name}) {
254
$ignore{$name}--;
255
$ignore_used{$name}++;
256
$nowarn = 1;
257
if(!$ignore{$name}) {
258
# reached zero, enable again
259
enable_warn($name, $num, $file, $line);
260
}
261
}
262
263
if($nowarn) {
264
$suppressed++;
265
if($w) {
266
$swarnings++;
267
}
268
else {
269
$serrors++;
270
}
271
return;
272
}
273
274
if($w) {
275
$warnings++;
276
}
277
else {
278
$errors++;
279
}
280
281
$col++;
282
print "$file:$num:$col: $w: $msg ($name)\n";
283
print " $line\n";
284
285
if($col < 80) {
286
my $pref = (' ' x $col);
287
print "${pref}^\n";
288
}
289
}
290
291
$file = shift @ARGV;
292
293
while(defined $file) {
294
295
if($file =~ /^-D(.*)/) {
296
$dir = $1;
297
$file = shift @ARGV;
298
next;
299
}
300
elsif($file =~ /^-W(.*)/) {
301
$wlist .= " $1 ";
302
$file = shift @ARGV;
303
next;
304
}
305
elsif($file =~ /^-b(.*)/) {
306
$banfunc{$1} = $1;
307
print STDERR "ban use of \"$1\"\n";
308
$file = shift @ARGV;
309
next;
310
}
311
elsif($file =~ /^-a(.*)/) {
312
undef $banfunc{$1};
313
$file = shift @ARGV;
314
next;
315
}
316
elsif($file =~ /^-A(.+)/) {
317
push @alist, $1;
318
$file = shift @ARGV;
319
next;
320
}
321
elsif($file =~ /^-i([1-9])/) {
322
$indent = $1 + 0;
323
$file = shift @ARGV;
324
next;
325
}
326
elsif($file =~ /^-m([0-9]+)/) {
327
$max_column = $1 + 0;
328
$file = shift @ARGV;
329
next;
330
}
331
elsif($file =~ /^-v/) {
332
$verbose = 1;
333
$file = shift @ARGV;
334
next;
335
}
336
elsif($file =~ /^(-h|--help)/) {
337
undef $file;
338
last;
339
}
340
341
last;
342
}
343
344
if(!$file) {
345
print "checksrc.pl [option] <file1> [file2] ...\n";
346
print " Options:\n";
347
print " -A[rule] Accept this violation, can be used multiple times\n";
348
print " -a[func] Allow use of this function\n";
349
print " -b[func] Ban use of this function\n";
350
print " -D[DIR] Directory to prepend file names\n";
351
print " -h Show help output\n";
352
print " -W[file] Skip the given file - ignore all its flaws\n";
353
print " -i<n> Indent spaces. Default: 2\n";
354
print " -m<n> Maximum line length. Default: 79\n";
355
print " -v Verbose\n";
356
print "\nDetects and warns for these problems:\n";
357
my @allw = keys %warnings;
358
push @allw, keys %warnings_extended;
359
for my $w (sort @allw) {
360
if($warnings{$w}) {
361
printf (" %-18s: %s\n", $w, $warnings{$w});
362
}
363
else {
364
printf (" %-18s: %s[*]\n", $w, $warnings_extended{$w});
365
}
366
}
367
print " [*] = disabled by default\n";
368
369
print "\nDetects and bans use of these functions:\n";
370
for my $f (sort keys %banfunc) {
371
printf (" %-18s\n", $f);
372
}
373
exit;
374
}
375
376
readskiplist();
377
readlocalfile($file);
378
379
do {
380
if("$wlist" !~ / $file /) {
381
my $fullname = $file;
382
$fullname = "$dir/$file" if($fullname !~ '^\.?\.?/');
383
scanfile($fullname);
384
}
385
$file = shift @ARGV;
386
387
} while($file);
388
389
sub accept_violations {
390
for my $r (@alist) {
391
if(!$warnings{$r}) {
392
print "'$r' is not a warning to accept!\n";
393
exit;
394
}
395
$ignore{$r}=999999;
396
$ignore_used{$r}=0;
397
}
398
}
399
400
sub checksrc_clear {
401
undef %ignore;
402
undef %ignore_set;
403
undef @ignore_line;
404
}
405
406
sub checksrc_endoffile {
407
my ($file) = @_;
408
for(keys %ignore_set) {
409
if($ignore_set{$_} && !$ignore_used{$_}) {
410
checkwarn("UNUSEDIGNORE", $ignore_set{$_},
411
length($_)+11, $file,
412
$ignore_line[$ignore_set{$_}],
413
"Unused ignore: $_");
414
}
415
}
416
}
417
418
sub enable_warn {
419
my ($what, $line, $file, $l) = @_;
420
421
# switch it back on, but warn if not triggered!
422
if(!$ignore_used{$what}) {
423
checkwarn("UNUSEDIGNORE",
424
$line, length($what) + 11, $file, $l,
425
"No warning was inhibited!");
426
}
427
$ignore_set{$what}=0;
428
$ignore_used{$what}=0;
429
$ignore{$what}=0;
430
}
431
sub checksrc {
432
my ($cmd, $line, $file, $l) = @_;
433
if($cmd =~ / *([^ ]*) *(.*)/) {
434
my ($enable, $what) = ($1, $2);
435
$what =~ s: *\*/$::; # cut off end of C comment
436
# print "ENABLE $enable WHAT $what\n";
437
if($enable eq "disable") {
438
my ($warn, $scope)=($1, $2);
439
if($what =~ /([^ ]*) +(.*)/) {
440
($warn, $scope)=($1, $2);
441
}
442
else {
443
$warn = $what;
444
$scope = 1;
445
}
446
# print "IGNORE $warn for SCOPE $scope\n";
447
if($scope eq "all") {
448
$scope=999999;
449
}
450
451
# Comparing for a literal zero rather than the scalar value zero
452
# covers the case where $scope contains the ending '*' from the
453
# comment. If we use a scalar comparison (==) we induce warnings
454
# on non-scalar contents.
455
if($scope eq "0") {
456
checkwarn("BADCOMMAND",
457
$line, 0, $file, $l,
458
"Disable zero not supported, did you mean to enable?");
459
}
460
elsif($ignore_set{$warn}) {
461
checkwarn("BADCOMMAND",
462
$line, 0, $file, $l,
463
"$warn already disabled from line $ignore_set{$warn}");
464
}
465
else {
466
$ignore{$warn}=$scope;
467
$ignore_set{$warn}=$line;
468
$ignore_line[$line]=$l;
469
}
470
}
471
elsif($enable eq "enable") {
472
enable_warn($what, $line, $file, $l);
473
}
474
else {
475
checkwarn("BADCOMMAND",
476
$line, 0, $file, $l,
477
"Illegal !checksrc! command");
478
}
479
}
480
}
481
482
sub nostrings {
483
my ($str) = @_;
484
$str =~ s/\".*\"//g;
485
return $str;
486
}
487
488
sub scanfile {
489
my ($file) = @_;
490
491
my $line = 1;
492
my $prevl="";
493
my $prevpl="";
494
my $l = "";
495
my $prep = 0;
496
my $prevp = 0;
497
498
if($verbose) {
499
printf "Checking file: $file\n";
500
}
501
502
open(my $R, '<', $file) || die "failed to open $file";
503
504
my $incomment=0;
505
my @copyright=();
506
my %includes;
507
checksrc_clear(); # for file based ignores
508
accept_violations();
509
510
while(<$R>) {
511
$windows_os ? $_ =~ s/\r?\n$// : chomp;
512
my $l = $_;
513
my $ol = $l; # keep the unmodified line for error reporting
514
my $column = 0;
515
516
# check for !checksrc! commands
517
if($l =~ /\!checksrc\! (.*)/) {
518
my $cmd = $1;
519
checksrc($cmd, $line, $file, $l)
520
}
521
522
if($l =~ /^#line (\d+) \"([^\"]*)\"/) {
523
# a #line instruction
524
$file = $2;
525
$line = $1;
526
next;
527
}
528
529
# check for a copyright statement and save the years
530
if($l =~ /\* +copyright .* (\d\d\d\d|)/i) {
531
my $count = 0;
532
while($l =~ /([\d]{4})/g) {
533
push @copyright, {
534
year => $1,
535
line => $line,
536
col => index($l, $1),
537
code => $l
538
};
539
$count++;
540
}
541
if(!$count) {
542
# year-less
543
push @copyright, {
544
year => -1,
545
line => $line,
546
col => index($l, $1),
547
code => $l
548
};
549
}
550
}
551
552
# detect long lines
553
if(length($l) > $max_column && $l !~ / https:\/\//) {
554
checkwarn("LONGLINE", $line, length($l), $file, $l,
555
"Longer than $max_column columns");
556
}
557
# detect TAB characters
558
if($l =~ /^(.*)\t/) {
559
checkwarn("TABS",
560
$line, length($1), $file, $l, "Contains TAB character", 1);
561
}
562
# detect trailing whitespace
563
if($l =~ /^(.*)[ \t]+\z/) {
564
checkwarn("TRAILINGSPACE",
565
$line, length($1), $file, $l, "Trailing whitespace");
566
}
567
568
# no space after comment start
569
if($l =~ /^(.*)\/\*\w/) {
570
checkwarn("COMMENTNOSPACESTART",
571
$line, length($1) + 2, $file, $l,
572
"Missing space after comment start");
573
}
574
# no space at comment end
575
if($l =~ /^(.*)\w\*\//) {
576
checkwarn("COMMENTNOSPACEEND",
577
$line, length($1) + 1, $file, $l,
578
"Missing space end comment end");
579
}
580
581
if($l =~ /(.*)(FIXME|TODO)/) {
582
checkwarn("FIXME",
583
$line, length($1), $file, $l,
584
"Avoid $2 comments. Add to documentation instead");
585
}
586
# ------------------------------------------------------------
587
# Above this marker, the checks were done on lines *including*
588
# comments
589
# ------------------------------------------------------------
590
591
# strip off C89 comments
592
593
comment:
594
if(!$incomment) {
595
if($l =~ s/\/\*.*\*\// /g) {
596
# full /* comments */ were removed!
597
}
598
if($l =~ s/\/\*.*//) {
599
# start of /* comment was removed
600
$incomment = 1;
601
}
602
}
603
else {
604
if($l =~ s/.*\*\///) {
605
# end of comment */ was removed
606
$incomment = 0;
607
goto comment;
608
}
609
else {
610
# still within a comment
611
$l="";
612
}
613
}
614
615
# ------------------------------------------------------------
616
# Below this marker, the checks were done on lines *without*
617
# comments
618
# ------------------------------------------------------------
619
620
# prev line was a preprocessor **and** ended with a backslash
621
if($prep && ($prevpl =~ /\\ *\z/)) {
622
# this is still a preprocessor line
623
$prep = 1;
624
goto preproc;
625
}
626
$prep = 0;
627
628
# crude attempt to detect // comments without too many false
629
# positives
630
if($l =~ /^(([^"\*]*)[^:"]|)\/\//) {
631
checkwarn("CPPCOMMENTS",
632
$line, length($1), $file, $l, "\/\/ comment");
633
}
634
635
if($l =~ /^(\#\s*include\s+)([\">].*[>}"])/) {
636
my ($pre, $path) = ($1, $2);
637
if($includes{$path}) {
638
checkwarn("INCLUDEDUP",
639
$line, length($1), $file, $l, "duplicated include");
640
}
641
$includes{$path} = $l;
642
}
643
644
# detect and strip preprocessor directives
645
if($l =~ /^[ \t]*\#/) {
646
# preprocessor line
647
$prep = 1;
648
goto preproc;
649
}
650
651
my $nostr = nostrings($l);
652
# check spaces after for/if/while/function call
653
if($nostr =~ /^(.*)(for|if|while|switch| ([a-zA-Z0-9_]+)) \((.)/) {
654
my ($leading, $word, $extra, $first)=($1,$2,$3,$4);
655
if($1 =~ / *\#/) {
656
# this is a #if, treat it differently
657
}
658
elsif(defined $3 && $3 eq "return") {
659
# return must have a space
660
}
661
elsif(defined $3 && $3 eq "case") {
662
# case must have a space
663
}
664
elsif(($first eq "*") && ($word !~ /(for|if|while|switch)/)) {
665
# A "(*" beginning makes the space OK because it wants to
666
# allow function pointer declared
667
}
668
elsif($1 =~ / *typedef/) {
669
# typedefs can use space-paren
670
}
671
else {
672
checkwarn("SPACEBEFOREPAREN", $line, length($leading)+length($word), $file, $l,
673
"$word with space");
674
}
675
}
676
# check for '== NULL' in if/while conditions but not if the thing on
677
# the left of it is a function call
678
if($nostr =~ /^(.*)(if|while)(\(.*?)([!=]= NULL|NULL [!=]=)/) {
679
checkwarn("EQUALSNULL", $line,
680
length($1) + length($2) + length($3),
681
$file, $l, "we prefer !variable instead of \"== NULL\" comparisons");
682
}
683
684
# check for '!= 0' in if/while conditions but not if the thing on
685
# the left of it is a function call
686
if($nostr =~ /^(.*)(if|while)(\(.*[^)]) != 0[^x]/) {
687
checkwarn("NOTEQUALSZERO", $line,
688
length($1) + length($2) + length($3),
689
$file, $l, "we prefer if(rc) instead of \"rc != 0\" comparisons");
690
}
691
692
# check spaces in 'do {'
693
if($nostr =~ /^( *)do( *)\{/ && length($2) != 1) {
694
checkwarn("DOBRACE", $line, length($1) + 2, $file, $l, "one space after do before brace");
695
}
696
# check spaces in 'do {'
697
elsif($nostr =~ /^( *)\}( *)while/ && length($2) != 1) {
698
checkwarn("BRACEWHILE", $line, length($1) + 2, $file, $l, "one space between brace and while");
699
}
700
if($nostr =~ /^((.*\s)(if) *\()(.*)\)(.*)/) {
701
my $pos = length($1);
702
my $postparen = $5;
703
my $cond = $4;
704
if($cond =~ / = /) {
705
checkwarn("ASSIGNWITHINCONDITION",
706
$line, $pos+1, $file, $l,
707
"assignment within conditional expression");
708
}
709
my $temp = $cond;
710
$temp =~ s/\(//g; # remove open parens
711
my $openc = length($cond) - length($temp);
712
713
$temp = $cond;
714
$temp =~ s/\)//g; # remove close parens
715
my $closec = length($cond) - length($temp);
716
my $even = $openc == $closec;
717
718
if($l =~ / *\#/) {
719
# this is a #if, treat it differently
720
}
721
elsif($even && $postparen &&
722
($postparen !~ /^ *$/) && ($postparen !~ /^ *[,{&|\\]+/)) {
723
checkwarn("ONELINECONDITION",
724
$line, length($l)-length($postparen), $file, $l,
725
"conditional block on the same line");
726
}
727
}
728
# check spaces after open parentheses
729
if($l =~ /^(.*[a-z])\( /i) {
730
checkwarn("SPACEAFTERPAREN",
731
$line, length($1)+1, $file, $l,
732
"space after open parenthesis");
733
}
734
735
# check spaces before Logical AND operator
736
if($nostr =~ /^(.*)\w&&/i) {
737
checkwarn("NOSPACEAND",
738
$line, length($1)+1, $file, $l,
739
"missing space before Logical AND");
740
}
741
742
# check spaces after Logical AND operator
743
if($nostr =~ /^(.*&&)\w/i) {
744
checkwarn("NOSPACEAND",
745
$line, length($1), $file, $l,
746
"missing space after Logical AND");
747
}
748
749
# check spaces before colon
750
if($nostr =~ /^(.*[^']\?[^'].*)(\w|\)|\]|')\:/i) {
751
my $m = $1;
752
my $e = $nostr;
753
$e =~ s/'(.)':'(.)'/$1:$2/g; # eliminate chars quotes that surround colon
754
$e =~ s/':'//g; # ignore these
755
if($e =~ /^(.*[^']\?[^'].*)(\w|\)|\]|')\:/i) {
756
checkwarn("NOSPACEC",
757
$line, length($m)+1, $file, $l,
758
"missing space before colon");
759
}
760
}
761
# check spaces after colon
762
if($nostr =~ /^(.*[^'"]\?[^'"].*)\:(\w|\)|\]|')/i) {
763
my $m = $1;
764
my $e = $nostr;
765
$e =~ s/'(.)':'(.)'/$1:$2/g; # eliminate chars quotes that surround colon
766
$e =~ s/':'//g; # ignore these
767
if($e =~ /^(.*[^'"]\?[^'"].*)\:(\w|\)|\]|')/i) {
768
checkwarn("NOSPACEC",
769
$line, length($m)+1, $file, $l,
770
"missing space after colon");
771
}
772
}
773
774
# check spaces before question mark
775
if($nostr =~ /^(.*)(\w|\)|\]|')\?/i) {
776
my $m = $1;
777
my $e = $nostr;
778
$e =~ s/'?'//g; # ignore these
779
if($e =~ /^(.*)(\w|\)|\]|')\?/i) {
780
checkwarn("NOSPACEQ",
781
$line, length($m)+1, $file, $l,
782
"missing space before question mark");
783
}
784
}
785
# check spaces after question mark
786
if($nostr =~ /^(.*)\?\w/i) {
787
checkwarn("NOSPACEQ",
788
$line, length($1)+1, $file, $l,
789
"missing space after question mark");
790
}
791
792
# check spaces before less or greater than
793
if($nostr =~ /^(.*)(\w|\)|\])[<>]/) {
794
checkwarn("NOSPACETHAN",
795
$line, length($1)+1, $file, $l,
796
"missing space before less or greater than");
797
}
798
# check spaces after less or greater than
799
if($nostr =~ /^(.*)[^-][<>](\w|\(|\[)/) {
800
checkwarn("NOSPACETHAN",
801
$line, length($1)+1, $file, $l,
802
"missing space after less or greater than");
803
}
804
805
# check spaces before close parentheses, unless it was a space or a
806
# close parenthesis!
807
if($l =~ /(.*[^\) ]) \)/) {
808
checkwarn("SPACEBEFORECLOSE",
809
$line, length($1)+1, $file, $l,
810
"space before close parenthesis");
811
}
812
813
# check spaces before comma!
814
if($l =~ /(.*[^ ]) ,/) {
815
checkwarn("SPACEBEFORECOMMA",
816
$line, length($1)+1, $file, $l,
817
"space before comma");
818
}
819
820
# check for "return(" without space
821
if($l =~ /^(.*\W)return\(/) {
822
if($1 =~ / *\#/) {
823
# this is a #if, treat it differently
824
}
825
else {
826
checkwarn("RETURNNOSPACE", $line, length($1)+6, $file, $l,
827
"return without space before paren");
828
}
829
}
830
831
# check for "return" with parentheses around just a value/name
832
if($l =~ /^(.*\W)return \(\w*\);/) {
833
checkwarn("RETURNPAREN", $line, length($1)+7, $file, $l,
834
"return with paren");
835
}
836
837
# check for "sizeof" without parenthesis
838
if(($l =~ /^(.*)sizeof *([ (])/) && ($2 ne "(")) {
839
if($1 =~ / *\#/) {
840
# this is a #if, treat it differently
841
}
842
else {
843
checkwarn("SIZEOFNOPAREN", $line, length($1)+6, $file, $l,
844
"sizeof without parenthesis");
845
}
846
}
847
848
# check for comma without space
849
if($l =~ /^(.*),[^ \n]/) {
850
my $pref=$1;
851
my $ign=0;
852
if($pref =~ / *\#/) {
853
# this is a #if, treat it differently
854
$ign=1;
855
}
856
elsif($pref =~ /\/\*/) {
857
# this is a comment
858
$ign=1;
859
}
860
elsif($pref =~ /[\"\']/) {
861
$ign = 1;
862
# There is a quote here, figure out whether the comma is
863
# within a string or '' or not.
864
if($pref =~ /\"/) {
865
# within a string
866
}
867
elsif($pref =~ /\'$/) {
868
# a single letter
869
}
870
else {
871
$ign = 0;
872
}
873
}
874
if(!$ign) {
875
checkwarn("COMMANOSPACE", $line, length($pref)+1, $file, $l,
876
"comma without following space");
877
}
878
}
879
880
# check for "} else"
881
if($l =~ /^(.*)\} *else/) {
882
checkwarn("BRACEELSE",
883
$line, length($1), $file, $l, "else after closing brace on same line");
884
}
885
# check for "){"
886
if($l =~ /^(.*)\)\{/) {
887
checkwarn("PARENBRACE",
888
$line, length($1)+1, $file, $l, "missing space after close paren");
889
}
890
# check for "^{" with an empty line before it
891
if(($l =~ /^\{/) && ($prevl =~ /^[ \t]*\z/)) {
892
checkwarn("EMPTYLINEBRACE",
893
$line, 0, $file, $l, "empty line before open brace");
894
}
895
896
# check for space before the semicolon last in a line
897
if($l =~ /^(.*[^ ].*) ;$/) {
898
checkwarn("SPACESEMICOLON",
899
$line, length($1), $file, $ol, "no space before semicolon");
900
}
901
902
# check for space before the colon in a switch label
903
if($l =~ /^( *(case .+|default)) :/) {
904
checkwarn("SPACESWITCHCOLON",
905
$line, length($1), $file, $ol, "no space before colon of switch label");
906
}
907
908
if($prevl !~ /\?\z/ && $l =~ /^ +([A-Za-z_][A-Za-z0-9_]*):$/ && $1 ne 'default') {
909
checkwarn("SPACEBEFORELABEL",
910
$line, length($1), $file, $ol, "no space before label");
911
}
912
913
# scan for use of banned functions
914
my $bl = $l;
915
again:
916
if((($l =~ /^(.*?\W)(\w+)(\s*\()/x) && $banfunc{$2}) ||
917
(($l =~ /^(.*?\()(\w+)(\s*\()/x) && $banfunc{$2})) {
918
my $bad = $2;
919
my $prefix = $1;
920
my $suff = $3;
921
checkwarn("BANNEDFUNC",
922
$line, length($prefix), $file, $ol,
923
"use of $bad is banned");
924
my $search = quotemeta($prefix . $bad . $suff);
925
my $replace = $prefix . 'x' x (length($bad) + 1);
926
$l =~ s/$search/$replace/;
927
goto again;
928
}
929
$l = $bl; # restore to pre-bannedfunc content
930
931
if($warnings{"STDERR"}) {
932
# scan for use of banned stderr. This is not a BANNEDFUNC to
933
# allow for individual enable/disable of this warning.
934
if($l =~ /^([^\"-]*\W)(stderr)[^\"_]/x) {
935
if($1 !~ /^ *\#/) {
936
# skip preprocessor lines
937
checkwarn("STDERR",
938
$line, length($1), $file, $ol,
939
"use of $2 is banned (use tool_stderr instead)");
940
}
941
}
942
}
943
944
# scan for use of non-binary fopen without the macro
945
if($l =~ /^(.*\W)(curlx_fopen|CURLX_FOPEN_LOW)\s*\([^,]*, *\"([^"]*)/) {
946
my $mode = $3;
947
if($mode !~ /b/) {
948
checkwarn("FOPENMODE",
949
$line, length($1), $file, $ol,
950
"use of non-binary fopen without FOPEN_* macro: $mode");
951
}
952
}
953
954
# check for open brace first on line but not first column only alert
955
# if previous line ended with a close paren and it wasn't a cpp line
956
if(($prevl =~ /\)\z/) && ($l =~ /^( +)\{/) && !$prevp) {
957
checkwarn("BRACEPOS",
958
$line, length($1), $file, $ol, "badly placed open brace");
959
}
960
961
# if the previous line starts with if/while/for AND ends with an open
962
# brace, or an else statement, check that this line is indented $indent
963
# more steps, if not a cpp line
964
if(!$prevp && ($prevl =~ /^( *)((if|while|for)\(.*\{|else)\z/)) {
965
my $first = length($1);
966
# this line has some character besides spaces
967
if($l =~ /^( *)[^ ]/) {
968
my $second = length($1);
969
my $expect = $first+$indent;
970
if($expect != $second) {
971
my $diff = $second - $first;
972
checkwarn("INDENTATION", $line, length($1), $file, $ol,
973
"not indented $indent steps (uses $diff)");
974
}
975
}
976
}
977
978
# if the previous line starts with if/while/for AND ends with a closed
979
# parenthesis and there's an equal number of open and closed
980
# parentheses, check that this line is indented $indent more steps, if
981
# not a cpp line
982
elsif(!$prevp && ($prevl =~ /^( *)(if|while|for)(\(.*\))\z/)) {
983
my $first = length($1);
984
my $op = $3;
985
my $cl = $3;
986
987
$op =~ s/[^(]//g;
988
$cl =~ s/[^)]//g;
989
990
if(length($op) == length($cl)) {
991
# this line has some character besides spaces
992
if($l =~ /^( *)[^ ]/) {
993
my $second = length($1);
994
my $expect = $first+$indent;
995
if($expect != $second) {
996
my $diff = $second - $first;
997
checkwarn("INDENTATION", $line, length($1), $file, $ol,
998
"not indented $indent steps (uses $diff)");
999
}
1000
}
1001
}
1002
}
1003
1004
# check for 'char * name'
1005
if(($l =~ /(^.*(char|int|long|void|CURL|CURLM|CURLMsg|[cC]url_[A-Za-z_]+|struct [a-zA-Z_]+) *(\*+)) (\w+)/) && ($4 !~ /^(const|volatile)$/)) {
1006
checkwarn("ASTERISKSPACE",
1007
$line, length($1), $file, $ol,
1008
"space after declarative asterisk");
1009
}
1010
# check for 'char*'
1011
if(($l =~ /(^.*(char|int|long|void|curl_slist|CURL|CURLM|CURLMsg|curl_httppost|sockaddr_in|FILE)\*)/)) {
1012
checkwarn("ASTERISKNOSPACE",
1013
$line, length($1)-1, $file, $ol,
1014
"no space before asterisk");
1015
}
1016
1017
# check for 'void func() {', but avoid false positives by requiring
1018
# both an open and closed parentheses before the open brace
1019
if($l =~ /^((\w).*)\{\z/) {
1020
my $k = $1;
1021
$k =~ s/const *//;
1022
$k =~ s/static *//;
1023
if($k =~ /\(.*\)/) {
1024
checkwarn("BRACEPOS",
1025
$line, length($l)-1, $file, $ol,
1026
"wrongly placed open brace");
1027
}
1028
}
1029
1030
# check for equals sign without spaces next to it
1031
if($nostr =~ /(.*)\=[a-z0-9]/i) {
1032
checkwarn("EQUALSNOSPACE",
1033
$line, length($1)+1, $file, $ol,
1034
"no space after equals sign");
1035
}
1036
# check for equals sign without spaces before it
1037
elsif($nostr =~ /(.*)[a-z0-9]\=/i) {
1038
checkwarn("NOSPACEEQUALS",
1039
$line, length($1)+1, $file, $ol,
1040
"no space before equals sign");
1041
}
1042
1043
# check for plus signs without spaces next to it
1044
if($nostr =~ /(.*)[^+]\+[a-z0-9]/i) {
1045
checkwarn("PLUSNOSPACE",
1046
$line, length($1)+1, $file, $ol,
1047
"no space after plus sign");
1048
}
1049
# check for plus sign without spaces before it
1050
elsif($nostr =~ /(.*)[a-z0-9]\+[^+]/i) {
1051
checkwarn("NOSPACEPLUS",
1052
$line, length($1)+1, $file, $ol,
1053
"no space before plus sign");
1054
}
1055
1056
# check for semicolons without space next to it
1057
if($nostr =~ /(.*)\;[a-z0-9]/i) {
1058
checkwarn("SEMINOSPACE",
1059
$line, length($1)+1, $file, $ol,
1060
"no space after semicolon");
1061
}
1062
1063
# typedef struct ... {
1064
if($nostr =~ /^(.*)typedef struct.*{/) {
1065
checkwarn("TYPEDEFSTRUCT",
1066
$line, length($1)+1, $file, $ol,
1067
"typedef'ed struct");
1068
}
1069
1070
if($nostr =~ /(.*)! +(\w|\()/) {
1071
checkwarn("EXCLAMATIONSPACE",
1072
$line, length($1)+1, $file, $ol,
1073
"space after exclamation mark");
1074
}
1075
1076
if($nostr =~ /(.*)\b(EACCES|EADDRINUSE|EADDRNOTAVAIL|EAFNOSUPPORT|EBADF|ECONNREFUSED|ECONNRESET|EINPROGRESS|EINTR|EINVAL|EISCONN|EMSGSIZE|ENOMEM|ETIMEDOUT|EWOULDBLOCK)\b/) {
1077
checkwarn("ERRNOVAR",
1078
$line, length($1), $file, $ol,
1079
"use of bare errno define $2, use SOCK$2");
1080
}
1081
1082
# check for more than one consecutive space before open brace or
1083
# question mark. Skip lines containing strings since they make it hard
1084
# due to artificially getting multiple spaces
1085
if(($l eq $nostr) &&
1086
$nostr =~ /^(.*(\S)) + [{?]/i) {
1087
checkwarn("MULTISPACE",
1088
$line, length($1)+1, $file, $ol,
1089
"multiple spaces");
1090
}
1091
preproc:
1092
if($prep) {
1093
# scan for use of banned symbols on a preprocessor line
1094
if($l =~ /^(^|.*\W)
1095
(WIN32)
1096
(\W|$)
1097
/x) {
1098
checkwarn("BANNEDPREPROC",
1099
$line, length($1), $file, $ol,
1100
"use of $2 is banned from preprocessor lines" .
1101
(($2 eq "WIN32") ? ", use _WIN32 instead" : ""));
1102
}
1103
}
1104
$line++;
1105
$prevp = $prep;
1106
$prevl = $ol if(!$prep);
1107
$prevpl = $ol if($prep);
1108
}
1109
1110
if(!scalar(@copyright)) {
1111
checkwarn("COPYRIGHT", 1, 0, $file, "", "Missing copyright statement", 1);
1112
}
1113
1114
# COPYRIGHTYEAR is an extended warning so we must first see if it has been
1115
# enabled in .checksrc
1116
if(defined($warnings{"COPYRIGHTYEAR"})) {
1117
# The check for updated copyrightyear is overly complicated in order to
1118
# not punish current hacking for past sins. The copyright years are
1119
# right now a bit behind, so enforcing copyright year checking on all
1120
# files would cause hundreds of errors. Instead we only look at files
1121
# which are tracked in the Git repo and edited in the workdir, or
1122
# committed locally on the branch without being in upstream master.
1123
#
1124
# The simple and naive test is to simply check for the current year,
1125
# but updating the year even without an edit is against project policy
1126
# (and it would fail every file on January 1st).
1127
#
1128
# A rather more interesting, and correct, check would be to not test
1129
# only locally committed files but inspect all files wrt the year of
1130
# their last commit. Removing the `git rev-list origin/master..HEAD`
1131
# condition below will enforce copyright year checks against the year
1132
# the file was last committed (and thus edited to some degree).
1133
my $commityear = undef;
1134
@copyright = sort {$$b{year} cmp $$a{year}} @copyright;
1135
1136
# if the file is modified, assume commit year this year
1137
if(`git status -s -- "$file"` =~ /^ [MARCU]/) {
1138
$commityear = (localtime(time))[5] + 1900;
1139
}
1140
else {
1141
# min-parents=1 to ignore wrong initial commit in truncated repos
1142
my $grl = `git rev-list --max-count=1 --min-parents=1 --timestamp HEAD -- "$file"`;
1143
if($grl) {
1144
chomp $grl;
1145
$commityear = (localtime((split(/ /, $grl))[0]))[5] + 1900;
1146
}
1147
}
1148
1149
if(defined($commityear) && scalar(@copyright) &&
1150
$copyright[0]{year} != $commityear) {
1151
checkwarn("COPYRIGHTYEAR", $copyright[0]{line}, $copyright[0]{col},
1152
$file, $copyright[0]{code},
1153
"Copyright year out of date, should be $commityear, " .
1154
"is $copyright[0]{year}", 1);
1155
}
1156
}
1157
1158
if($incomment) {
1159
checkwarn("OPENCOMMENT", 1, 0, $file, "", "Missing closing comment", 1);
1160
}
1161
1162
checksrc_endoffile($file);
1163
1164
close($R);
1165
1166
}
1167
1168
1169
if($errors || $warnings || $verbose) {
1170
printf "checksrc: %d errors and %d warnings\n", $errors, $warnings;
1171
if($suppressed) {
1172
printf "checksrc: %d errors and %d warnings suppressed\n",
1173
$serrors,
1174
$swarnings;
1175
}
1176
if($errors || $warnings) {
1177
exit 5; # return failure
1178
}
1179
}
1180
1181