checkpatch.pl 188 KB
Newer Older
1
#!/usr/bin/env perl
2
# (c) 2001, Dave Jones. (the file handling bit)
3
# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
4
# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
5
# (c) 2008-2010 Andy Whitcroft <apw@canonical.com>
6 7 8
# Licensed under the terms of the GNU GPL License version 2

use strict;
9
use warnings;
10
use POSIX;
11 12
use File::Basename;
use Cwd 'abs_path';
13
use Term::ANSIColor qw(:constants);
14 15

my $P = $0;
16
my $D = dirname(abs_path($P));
17

18
my $V = '0.32';
19 20 21 22 23 24 25

use Getopt::Long qw(:config no_auto_abbrev);

my $quiet = 0;
my $tree = 1;
my $chk_signoff = 1;
my $chk_patch = 1;
26
my $tst_only;
27
my $emacs = 0;
28
my $terse = 0;
29
my $showfile = 0;
30
my $file = 0;
31
my $git = 0;
32
my %git_commits = ();
33
my $check = 0;
34
my $check_orig = 0;
35 36
my $summary = 1;
my $mailback = 0;
37
my $summary_file = 0;
38
my $show_types = 0;
39
my $list_types = 0;
40
my $fix = 0;
41
my $fix_inplace = 0;
42
my $root;
43
my %debug;
44
my %camelcase = ();
45 46 47
my %use_type = ();
my @use = ();
my %ignore_type = ();
48
my @ignore = ();
49
my $help = 0;
50
my $configuration_file = ".checkpatch.conf";
51
my $max_line_length = 80;
52 53
my $ignore_perl_version = 0;
my $minimum_perl_version = 5.10.0;
54
my $min_conf_desc_length = 4;
55
my $spelling_file = "$D/spelling.txt";
56
my $codespell = 0;
57
my $codespellfile = "/usr/share/codespell/dictionary.txt";
58
my $conststructsfile = "$D/const_structs.checkpatch";
59
my $typedefsfile = "";
60
my $color = "auto";
61
my $allow_c99_comments = 1;
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

sub help {
	my ($exitcode) = @_;

	print << "EOM";
Usage: $P [OPTION]... [FILE]...
Version: $V

Options:
  -q, --quiet                quiet
  --no-tree                  run without a kernel tree
  --no-signoff               do not check for 'Signed-off-by' line
  --patch                    treat FILE as patchfile (default)
  --emacs                    emacs compile window format
  --terse                    one line per report
77
  --showfile                 emit diffed file position, not input file position
78 79 80 81 82 83 84 85 86 87
  -g, --git                  treat FILE as a single commit or git revision range
                             single git commit with:
                               <rev>
                               <rev>^
                               <rev>~n
                             multiple git commits with:
                               <rev1>..<rev2>
                               <rev1>...<rev2>
                               <rev>-<count>
                             git merges are ignored
88 89
  -f, --file                 treat FILE as regular source file
  --subjective, --strict     enable more subjective tests
90
  --list-types               list the possible message types
91
  --types TYPE(,TYPE2...)    show only these comma separated message types
92
  --ignore TYPE(,TYPE2...)   ignore various comma separated message types
93
  --show-types               show the specific message type in the output
94
  --max-line-length=n        set the maximum line length, if exceeded, warn
95
  --min-conf-desc-length=n   set the min description length, if shorter, warn
96 97 98 99 100 101 102 103 104
  --root=PATH                PATH to the kernel tree root
  --no-summary               suppress the per-file summary
  --mailback                 only produce a report in case of warnings/errors
  --summary-file             include the filename in summary
  --debug KEY=[0|1]          turn on/off debugging of KEY, where KEY is one of
                             'values', 'possible', 'type', and 'attr' (default
                             is all off)
  --test-only=WORD           report only warnings/errors containing WORD
                             literally
105 106 107 108 109
  --fix                      EXPERIMENTAL - may create horrible results
                             If correctable single-line errors exist, create
                             "<inputfile>.EXPERIMENTAL-checkpatch-fixes"
                             with potential errors corrected to the preferred
                             checkpatch style
110 111 112
  --fix-inplace              EXPERIMENTAL - may create horrible results
                             Is the same as --fix, but overwrites the input
                             file.  It's your fault if there's no backup or git
113 114
  --ignore-perl-version      override checking of perl version.  expect
                             runtime errors.
115
  --codespell                Use the codespell dictionary for spelling/typos
116
                             (default:/usr/share/codespell/dictionary.txt)
117
  --codespellfile            Use this codespell dictionary
118
  --typedefsfile             Read additional types from this file
119 120
  --color[=WHEN]             Use colors 'always', 'never', or only when output
                             is a terminal ('auto'). Default is 'auto'.
121 122 123 124 125 126 127 128
  -h, --help, --version      display this help and exit

When FILE is - read standard input.
EOM

	exit($exitcode);
}

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
sub uniq {
	my %seen;
	return grep { !$seen{$_}++ } @_;
}

sub list_types {
	my ($exitcode) = @_;

	my $count = 0;

	local $/ = undef;

	open(my $script, '<', abs_path($P)) or
	    die "$P: Can't read '$P' $!\n";

	my $text = <$script>;
	close($script);

	my @types = ();
148 149
	# Also catch when type or level is passed through a variable
	for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) {
150 151 152 153 154 155 156 157 158 159 160
		push (@types, $_);
	}
	@types = sort(uniq(@types));
	print("#\tMessage type\n\n");
	foreach my $type (@types) {
		print(++$count . "\t" . $type . "\n");
	}

	exit($exitcode);
}

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
my $conf = which_conf($configuration_file);
if (-f $conf) {
	my @conf_args;
	open(my $conffile, '<', "$conf")
	    or warn "$P: Can't find a readable $configuration_file file $!\n";

	while (<$conffile>) {
		my $line = $_;

		$line =~ s/\s*\n?$//g;
		$line =~ s/^\s*//g;
		$line =~ s/\s+/ /g;

		next if ($line =~ m/^\s*#/);
		next if ($line =~ m/^\s*$/);

		my @words = split(" ", $line);
		foreach my $word (@words) {
			last if ($word =~ m/^#/);
			push (@conf_args, $word);
		}
	}
	close($conffile);
	unshift(@ARGV, @conf_args) if @conf_args;
}

187 188 189 190 191 192 193 194
# Perl's Getopt::Long allows options to take optional arguments after a space.
# Prevent --color by itself from consuming other arguments
foreach (@ARGV) {
	if ($_ eq "--color" || $_ eq "-color") {
		$_ = "--color=$color";
	}
}

195
GetOptions(
196
	'q|quiet+'	=> \$quiet,
197 198 199
	'tree!'		=> \$tree,
	'signoff!'	=> \$chk_signoff,
	'patch!'	=> \$chk_patch,
200
	'emacs!'	=> \$emacs,
201
	'terse!'	=> \$terse,
202
	'showfile!'	=> \$showfile,
203
	'f|file!'	=> \$file,
204
	'g|git!'	=> \$git,
205 206
	'subjective!'	=> \$check,
	'strict!'	=> \$check,
207
	'ignore=s'	=> \@ignore,
208
	'types=s'	=> \@use,
209
	'show-types!'	=> \$show_types,
210
	'list-types!'	=> \$list_types,
211
	'max-line-length=i' => \$max_line_length,
212
	'min-conf-desc-length=i' => \$min_conf_desc_length,
213
	'root=s'	=> \$root,
214 215
	'summary!'	=> \$summary,
	'mailback!'	=> \$mailback,
216
	'summary-file!'	=> \$summary_file,
217
	'fix!'		=> \$fix,
218
	'fix-inplace!'	=> \$fix_inplace,
219
	'ignore-perl-version!' => \$ignore_perl_version,
220
	'debug=s'	=> \%debug,
221
	'test-only=s'	=> \$tst_only,
222 223
	'codespell!'	=> \$codespell,
	'codespellfile=s'	=> \$codespellfile,
224
	'typedefsfile=s'	=> \$typedefsfile,
225 226 227
	'color=s'	=> \$color,
	'no-color'	=> \$color,	#keep old behaviors of -nocolor
	'nocolor'	=> \$color,	#keep old behaviors of -nocolor
228 229 230 231 232
	'h|help'	=> \$help,
	'version'	=> \$help
) or help(1);

help(0) if ($help);
233

234 235
list_types(0) if ($list_types);

236
$fix = 1 if ($fix_inplace);
237
$check_orig = $check;
238

239 240
my $exit = 0;

241 242 243 244 245 246 247
if ($^V && $^V lt $minimum_perl_version) {
	printf "$P: requires at least perl version %vd\n", $minimum_perl_version;
	if (!$ignore_perl_version) {
		exit(1);
	}
}

248
#if no filenames are given, push '-' to read patch from stdin
249
if ($#ARGV < 0) {
250
	push(@ARGV, '-');
251 252
}

253 254 255 256 257 258 259 260 261 262 263 264
if ($color =~ /^[01]$/) {
	$color = !$color;
} elsif ($color =~ /^always$/i) {
	$color = 1;
} elsif ($color =~ /^never$/i) {
	$color = 0;
} elsif ($color =~ /^auto$/i) {
	$color = (-t STDOUT);
} else {
	die "Invalid color mode: $color\n";
}

265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
sub hash_save_array_words {
	my ($hashRef, $arrayRef) = @_;

	my @array = split(/,/, join(',', @$arrayRef));
	foreach my $word (@array) {
		$word =~ s/\s*\n?$//g;
		$word =~ s/^\s*//g;
		$word =~ s/\s+/ /g;
		$word =~ tr/[a-z]/[A-Z]/;

		next if ($word =~ m/^\s*#/);
		next if ($word =~ m/^\s*$/);

		$hashRef->{$word}++;
	}
}
281

282 283
sub hash_show_words {
	my ($hashRef, $prefix) = @_;
284

285
	if (keys %$hashRef) {
286
		print "\nNOTE: $prefix message types:";
287
		foreach my $word (sort keys %$hashRef) {
288 289
			print " $word";
		}
290
		print "\n";
291
	}
292 293
}

294 295 296
hash_save_array_words(\%ignore_type, \@ignore);
hash_save_array_words(\%use_type, \@use);

297 298
my $dbg_values = 0;
my $dbg_possible = 0;
299
my $dbg_type = 0;
300
my $dbg_attr = 0;
301
for my $key (keys %debug) {
302 303 304
	## no critic
	eval "\${dbg_$key} = '$debug{$key}';";
	die "$@" if ($@);
305 306
}

307 308
my $rpt_cleaners = 0;

309 310 311 312 313
if ($terse) {
	$emacs = 1;
	$quiet++;
}

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
if ($tree) {
	if (defined $root) {
		if (!top_of_kernel_tree($root)) {
			die "$P: $root: --root does not point at a valid tree\n";
		}
	} else {
		if (top_of_kernel_tree('.')) {
			$root = '.';
		} elsif ($0 =~ m@(.*)/scripts/[^/]*$@ &&
						top_of_kernel_tree($1)) {
			$root = $1;
		}
	}

	if (!defined $root) {
		print "Must be run from the top-level dir. of a kernel tree\n";
		exit(2);
	}
332 333
}

334 335
my $emitted_corrupt = 0;

336 337 338 339
our $Ident	= qr{
			[A-Za-z_][A-Za-z\d_]*
			(?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)*
		}x;
340 341 342 343 344 345 346 347
our $Storage	= qr{extern|static|asmlinkage};
our $Sparse	= qr{
			__user|
			__kernel|
			__force|
			__iomem|
			__must_check|
			__init_refok|
348
			__kprobes|
349
			__ref|
350 351
			__rcu|
			__private
352
		}x;
353 354 355 356 357
our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)};
our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)};
our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)};
our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)};
our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit};
358

359 360
# Notes to $Attribute:
# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
361 362
our $Attribute	= qr{
			const|
363 364 365
			__percpu|
			__nocast|
			__safe|
366
			__bitwise|
367 368 369 370 371 372 373 374
			__packed__|
			__packed2__|
			__naked|
			__maybe_unused|
			__always_unused|
			__noreturn|
			__used|
			__cold|
375
			__pure|
376 377
			__noclone|
			__deprecated|
378 379
			__read_mostly|
			__kprobes|
380
			$InitAttribute|
381 382
			____cacheline_aligned|
			____cacheline_aligned_in_smp|
383 384
			____cacheline_internodealigned_in_smp|
			__weak
385
		  }x;
386
our $Modifier;
387
our $Inline	= qr{inline|__always_inline|noinline|__inline|__inline__};
388 389 390
our $Member	= qr{->$Ident|\.$Ident|\[[^]]*\]};
our $Lval	= qr{$Ident(?:$Member)*};

391 392 393 394
our $Int_type	= qr{(?i)llu|ull|ll|lu|ul|l|u};
our $Binary	= qr{(?i)0b[01]+$Int_type?};
our $Hex	= qr{(?i)0x[0-9a-f]+$Int_type?};
our $Int	= qr{[0-9]+$Int_type?};
395
our $Octal	= qr{0[0-7]+$Int_type?};
396
our $String	= qr{"[X\t]*"};
397 398 399
our $Float_hex	= qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
our $Float_dec	= qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
our $Float_int	= qr{(?i)[0-9]+e-?[0-9]+[fl]?};
400
our $Float	= qr{$Float_hex|$Float_dec|$Float_int};
401
our $Constant	= qr{$Float|$Binary|$Octal|$Hex|$Int};
402
our $Assignment	= qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=};
403
our $Compare    = qr{<=|>=|==|!=|<|(?<!-)>};
404
our $Arithmetic = qr{\+|-|\*|\/|%};
405 406 407
our $Operators	= qr{
			<=|>=|==|!=|
			=>|->|<<|>>|<|>|!|~|
408
			&&|\|\||,|\^|\+\+|--|&|\||$Arithmetic
409 410
		  }x;

411 412
our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;

413
our $BasicType;
414
our $NonptrType;
415
our $NonptrTypeMisordered;
416
our $NonptrTypeWithAttr;
417
our $Type;
418
our $TypeMisordered;
419
our $Declare;
420
our $DeclareMisordered;
421

422 423
our $NON_ASCII_UTF8	= qr{
	[\xC2-\xDF][\x80-\xBF]               # non-overlong 2-byte
424 425 426 427 428 429 430 431
	|  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
	| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
	|  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
	|  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
	| [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
	|  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
}x;

432 433 434 435 436
our $UTF8	= qr{
	[\x09\x0A\x0D\x20-\x7E]              # ASCII
	| $NON_ASCII_UTF8
}x;

437
our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t};
438 439 440 441
our $typeOtherOSTypedefs = qr{(?x:
	u_(?:char|short|int|long) |          # bsd
	u(?:nchar|short|int|long)            # sysv
)};
442
our $typeKernelTypedefs = qr{(?x:
443
	(?:__)?(?:u|s|be|le)(?:8|16|32|64)|
444 445
	atomic_t
)};
446 447 448 449 450
our $typeTypedefs = qr{(?x:
	$typeC99Typedefs\b|
	$typeOtherOSTypedefs\b|
	$typeKernelTypedefs\b
)};
451

452 453
our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b};

454
our $logFunctions = qr{(?x:
455
	printk(?:_ratelimited|_once|_deferred_once|_deferred|)|
456
	(?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)|
457
	WARN(?:_RATELIMIT|_ONCE|)|
458
	panic|
459 460
	MODULE_[A-Z_]+|
	seq_vprintf|seq_printf|seq_puts
461 462
)};

463 464 465 466 467 468
our $signature_tags = qr{(?xi:
	Signed-off-by:|
	Acked-by:|
	Tested-by:|
	Reviewed-by:|
	Reported-by:|
469
	Suggested-by:|
470 471 472 473
	To:|
	Cc:
)};

474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
our @typeListMisordered = (
	qr{char\s+(?:un)?signed},
	qr{int\s+(?:(?:un)?signed\s+)?short\s},
	qr{int\s+short(?:\s+(?:un)?signed)},
	qr{short\s+int(?:\s+(?:un)?signed)},
	qr{(?:un)?signed\s+int\s+short},
	qr{short\s+(?:un)?signed},
	qr{long\s+int\s+(?:un)?signed},
	qr{int\s+long\s+(?:un)?signed},
	qr{long\s+(?:un)?signed\s+int},
	qr{int\s+(?:un)?signed\s+long},
	qr{int\s+(?:un)?signed},
	qr{int\s+long\s+long\s+(?:un)?signed},
	qr{long\s+long\s+int\s+(?:un)?signed},
	qr{long\s+long\s+(?:un)?signed\s+int},
	qr{long\s+long\s+(?:un)?signed},
	qr{long\s+(?:un)?signed},
);

493 494
our @typeList = (
	qr{void},
495 496 497 498 499 500 501 502 503
	qr{(?:(?:un)?signed\s+)?char},
	qr{(?:(?:un)?signed\s+)?short\s+int},
	qr{(?:(?:un)?signed\s+)?short},
	qr{(?:(?:un)?signed\s+)?int},
	qr{(?:(?:un)?signed\s+)?long\s+int},
	qr{(?:(?:un)?signed\s+)?long\s+long\s+int},
	qr{(?:(?:un)?signed\s+)?long\s+long},
	qr{(?:(?:un)?signed\s+)?long},
	qr{(?:un)?signed},
504 505 506 507 508 509 510 511 512
	qr{float},
	qr{double},
	qr{bool},
	qr{struct\s+$Ident},
	qr{union\s+$Ident},
	qr{enum\s+$Ident},
	qr{${Ident}_t},
	qr{${Ident}_handler},
	qr{${Ident}_handler_fn},
513
	@typeListMisordered,
514
);
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536

our $C90_int_types = qr{(?x:
	long\s+long\s+int\s+(?:un)?signed|
	long\s+long\s+(?:un)?signed\s+int|
	long\s+long\s+(?:un)?signed|
	(?:(?:un)?signed\s+)?long\s+long\s+int|
	(?:(?:un)?signed\s+)?long\s+long|
	int\s+long\s+long\s+(?:un)?signed|
	int\s+(?:(?:un)?signed\s+)?long\s+long|

	long\s+int\s+(?:un)?signed|
	long\s+(?:un)?signed\s+int|
	long\s+(?:un)?signed|
	(?:(?:un)?signed\s+)?long\s+int|
	(?:(?:un)?signed\s+)?long|
	int\s+long\s+(?:un)?signed|
	int\s+(?:(?:un)?signed\s+)?long|

	int\s+(?:un)?signed|
	(?:(?:un)?signed\s+)?int
)};

537
our @typeListFile = ();
538 539 540 541 542 543
our @typeListWithAttr = (
	@typeList,
	qr{struct\s+$InitAttribute\s+$Ident},
	qr{union\s+$InitAttribute\s+$Ident},
);

544 545 546
our @modifierList = (
	qr{fastcall},
);
547
our @modifierListFile = ();
548

549 550 551 552 553 554
our @mode_permission_funcs = (
	["module_param", 3],
	["module_param_(?:array|named|string)", 4],
	["module_param_array_named", 5],
	["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2],
	["proc_create(?:_data|)", 2],
555 556 557 558 559
	["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2],
	["IIO_DEV_ATTR_[A-Z_]+", 1],
	["SENSOR_(?:DEVICE_|)ATTR_2", 2],
	["SENSOR_TEMPLATE(?:_2|)", 3],
	["__ATTR", 2],
560 561
);

562 563 564 565 566 567 568
#Create a search pattern for all these functions to speed up a loop below
our $mode_perms_search = "";
foreach my $entry (@mode_permission_funcs) {
	$mode_perms_search .= '|' if ($mode_perms_search ne "");
	$mode_perms_search .= $entry->[0];
}

569 570 571 572 573 574 575 576
our $mode_perms_world_writable = qr{
	S_IWUGO		|
	S_IWOTH		|
	S_IRWXUGO	|
	S_IALLUGO	|
	0[0-7][0-7][2367]
}x;

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
our %mode_permission_string_types = (
	"S_IRWXU" => 0700,
	"S_IRUSR" => 0400,
	"S_IWUSR" => 0200,
	"S_IXUSR" => 0100,
	"S_IRWXG" => 0070,
	"S_IRGRP" => 0040,
	"S_IWGRP" => 0020,
	"S_IXGRP" => 0010,
	"S_IRWXO" => 0007,
	"S_IROTH" => 0004,
	"S_IWOTH" => 0002,
	"S_IXOTH" => 0001,
	"S_IRWXUGO" => 0777,
	"S_IRUGO" => 0444,
	"S_IWUGO" => 0222,
	"S_IXUGO" => 0111,
);

#Create a search pattern for all these strings to speed up a loop below
our $mode_perms_string_search = "";
foreach my $entry (keys %mode_permission_string_types) {
	$mode_perms_string_search .= '|' if ($mode_perms_string_search ne "");
	$mode_perms_string_search .= $entry;
}

603 604
our $allowed_asm_includes = qr{(?x:
	irq|
605 606 607
	memory|
	time|
	reboot
608 609 610
)};
# memory.h: ARM has a custom one

611 612 613 614
# Load common spelling mistakes and build regular expression list.
my $misspellings;
my %spelling_fix;

615 616 617
if (open(my $spelling, '<', $spelling_file)) {
	while (<$spelling>) {
		my $line = $_;
618

619 620
		$line =~ s/\s*\n?$//g;
		$line =~ s/^\s*//g;
621

622 623 624 625
		next if ($line =~ m/^\s*#/);
		next if ($line =~ m/^\s*$/);

		my ($suspect, $fix) = split(/\|\|/, $line);
626

627 628 629 630 631
		$spelling_fix{$suspect} = $fix;
	}
	close($spelling);
} else {
	warn "No typos will be found - file '$spelling_file': $!\n";
632 633
}

634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
if ($codespell) {
	if (open(my $spelling, '<', $codespellfile)) {
		while (<$spelling>) {
			my $line = $_;

			$line =~ s/\s*\n?$//g;
			$line =~ s/^\s*//g;

			next if ($line =~ m/^\s*#/);
			next if ($line =~ m/^\s*$/);
			next if ($line =~ m/, disabled/i);

			$line =~ s/,.*$//;

			my ($suspect, $fix) = split(/->/, $line);

			$spelling_fix{$suspect} = $fix;
		}
		close($spelling);
	} else {
		warn "No codespell typos will be found - file '$codespellfile': $!\n";
	}
}

$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix;

660 661
sub read_words {
	my ($wordsRef, $file) = @_;
662

663 664 665
	if (open(my $words, '<', $file)) {
		while (<$words>) {
			my $line = $_;
666

667 668
			$line =~ s/\s*\n?$//g;
			$line =~ s/^\s*//g;
669

670 671 672 673 674 675 676 677 678 679 680 681
			next if ($line =~ m/^\s*#/);
			next if ($line =~ m/^\s*$/);
			if ($line =~ /\s/) {
				print("$file: '$line' invalid - ignored\n");
				next;
			}

			$$wordsRef .= '|' if ($$wordsRef ne "");
			$$wordsRef .= $line;
		}
		close($file);
		return 1;
682
	}
683 684 685 686 687 688 689 690 691 692 693 694

	return 0;
}

my $const_structs = "";
read_words(\$const_structs, $conststructsfile)
    or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";

my $typeOtherTypedefs = "";
if (length($typedefsfile)) {
	read_words(\$typeOtherTypedefs, $typedefsfile)
	    or warn "No additional types will be considered - file '$typedefsfile': $!\n";
695
}
696
$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne "");
697

698
sub build_types {
699 700
	my $mods = "(?x:  \n" . join("|\n  ", (@modifierList, @modifierListFile)) . "\n)";
	my $all = "(?x:  \n" . join("|\n  ", (@typeList, @typeListFile)) . "\n)";
701
	my $Misordered = "(?x:  \n" . join("|\n  ", @typeListMisordered) . "\n)";
702
	my $allWithAttr = "(?x:  \n" . join("|\n  ", @typeListWithAttr) . "\n)";
703
	$Modifier	= qr{(?:$Attribute|$Sparse|$mods)};
704 705 706 707
	$BasicType	= qr{
				(?:$typeTypedefs\b)|
				(?:${all}\b)
		}x;
708
	$NonptrType	= qr{
709
			(?:$Modifier\s+|const\s+)*
710
			(?:
711
				(?:typeof|__typeof__)\s*\([^\)]*\)|
712
				(?:$typeTypedefs\b)|
713
				(?:${all}\b)
714
			)
715
			(?:\s+$Modifier|\s+const)*
716
		  }x;
717 718 719 720 721 722 723
	$NonptrTypeMisordered	= qr{
			(?:$Modifier\s+|const\s+)*
			(?:
				(?:${Misordered}\b)
			)
			(?:\s+$Modifier|\s+const)*
		  }x;
724 725 726 727 728 729 730 731 732
	$NonptrTypeWithAttr	= qr{
			(?:$Modifier\s+|const\s+)*
			(?:
				(?:typeof|__typeof__)\s*\([^\)]*\)|
				(?:$typeTypedefs\b)|
				(?:${allWithAttr}\b)
			)
			(?:\s+$Modifier|\s+const)*
		  }x;
733
	$Type	= qr{
734
			$NonptrType
735
			(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
736
			(?:\s+$Inline|\s+$Modifier)*
737
		  }x;
738 739 740 741 742
	$TypeMisordered	= qr{
			$NonptrTypeMisordered
			(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+)?
			(?:\s+$Inline|\s+$Modifier)*
		  }x;
743
	$Declare	= qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
744
	$DeclareMisordered	= qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered};
745 746
}
build_types();
747

748
our $Typecast	= qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*};
749 750 751 752 753 754

# Using $balanced_parens, $LvalOrFunc, or $FuncArg
# requires at least perl version v5.10.0
# Any use must be runtime checked with $^V

our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/;
755
our $LvalOrFunc	= qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*};
756
our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)};
757

758
our $declaration_macros = qr{(?x:
759
	(?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
760
	(?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
761 762 763
	(?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(
)};

764 765 766
sub deparenthesize {
	my ($string) = @_;
	return "" if (!defined($string));
767 768 769 770 771 772

	while ($string =~ /^\s*\(.*\)\s*$/) {
		$string =~ s@^\s*\(\s*@@;
		$string =~ s@\s*\)\s*$@@;
	}

773
	$string =~ s@\s+@ @g;
774

775 776 777
	return $string;
}

778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
sub seed_camelcase_file {
	my ($file) = @_;

	return if (!(-f $file));

	local $/;

	open(my $include_file, '<', "$file")
	    or warn "$P: Can't read '$file' $!\n";
	my $text = <$include_file>;
	close($include_file);

	my @lines = split('\n', $text);

	foreach my $line (@lines) {
		next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/);
		if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) {
			$camelcase{$1} = 1;
796 797 798
		} elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) {
			$camelcase{$1} = 1;
		} elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) {
799 800 801 802 803
			$camelcase{$1} = 1;
		}
	}
}

804 805 806
sub is_maintained_obsolete {
	my ($filename) = @_;

807
	return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl"));
808

809
	my $status = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
810 811 812 813

	return $status =~ /obsolete/i;
}

814 815 816 817 818
my $camelcase_seeded = 0;
sub seed_camelcase_includes {
	return if ($camelcase_seeded);

	my $files;
819 820 821 822
	my $camelcase_cache = "";
	my @include_files = ();

	$camelcase_seeded = 1;
823

824
	if (-e ".git") {
825 826
		my $git_last_include_commit = `git log --no-merges --pretty=format:"%h%n" -1 -- include`;
		chomp $git_last_include_commit;
827
		$camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit";
828
	} else {
829
		my $last_mod_date = 0;
830
		$files = `find $root/include -name "*.h"`;
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
		@include_files = split('\n', $files);
		foreach my $file (@include_files) {
			my $date = POSIX::strftime("%Y%m%d%H%M",
						   localtime((stat $file)[9]));
			$last_mod_date = $date if ($last_mod_date < $date);
		}
		$camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date";
	}

	if ($camelcase_cache ne "" && -f $camelcase_cache) {
		open(my $camelcase_file, '<', "$camelcase_cache")
		    or warn "$P: Can't read '$camelcase_cache' $!\n";
		while (<$camelcase_file>) {
			chomp;
			$camelcase{$_} = 1;
		}
		close($camelcase_file);

		return;
850
	}
851

852
	if (-e ".git") {
853 854 855 856
		$files = `git ls-files "include/*.h"`;
		@include_files = split('\n', $files);
	}

857 858 859
	foreach my $file (@include_files) {
		seed_camelcase_file($file);
	}
860

861
	if ($camelcase_cache ne "") {
862
		unlink glob ".checkpatch-camelcase.*";
863 864
		open(my $camelcase_file, '>', "$camelcase_cache")
		    or warn "$P: Can't write '$camelcase_cache' $!\n";
865 866 867 868 869
		foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) {
			print $camelcase_file ("$_\n");
		}
		close($camelcase_file);
	}
870 871
}

872 873 874 875 876 877 878 879 880
sub git_commit_info {
	my ($commit, $id, $desc) = @_;

	return ($id, $desc) if ((which("git") eq "") || !(-e ".git"));

	my $output = `git log --no-color --format='%H %s' -1 $commit 2>&1`;
	$output =~ s/^\s*//gm;
	my @lines = split("\n", $output);

881 882
	return ($id, $desc) if ($#lines < 0);

883 884 885 886 887 888 889 890 891 892 893
	if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous\./) {
# Maybe one day convert this block of bash into something that returns
# all matching commit ids, but it's very slow...
#
#		echo "checking commits $1..."
#		git rev-list --remotes | grep -i "^$1" |
#		while read line ; do
#		    git log --format='%H %s' -1 $line |
#		    echo "commit $(cut -c 1-12,41-)"
#		done
	} elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) {
894
		$id = undef;
895 896 897 898 899 900 901 902
	} else {
		$id = substr($lines[0], 0, 12);
		$desc = substr($lines[0], 41);
	}

	return ($id, $desc);
}

903 904
$chk_signoff = 0 if ($file);

905
my @rawlines = ();
906
my @lines = ();
907
my @fixed = ();
908 909
my @fixed_inserted = ();
my @fixed_deleted = ();
910 911
my $fixlinenr = -1;

912 913 914 915 916 917
# If input is git commits, extract all commits from the commit expressions.
# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'.
die "$P: No git repository found\n" if ($git && !-e ".git");

if ($git) {
	my @commits = ();
918
	foreach my $commit_expr (@ARGV) {
919
		my $git_range;
920 921
		if ($commit_expr =~ m/^(.*)-(\d+)$/) {
			$git_range = "-$2 $1";
922 923 924
		} elsif ($commit_expr =~ m/\.\./) {
			$git_range = "$commit_expr";
		} else {
925 926 927 928
			$git_range = "-1 $commit_expr";
		}
		my $lines = `git log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
		foreach my $line (split(/\n/, $lines)) {
929 930
			$line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
			next if (!defined($1) || !defined($2));
931 932 933 934
			my $sha1 = $1;
			my $subject = $2;
			unshift(@commits, $sha1);
			$git_commits{$sha1} = $subject;
935 936 937 938 939 940
		}
	}
	die "$P: no git commits after extraction!\n" if (@commits == 0);
	@ARGV = @commits;
}

941
my $vname;
942
for my $filename (@ARGV) {
943
	my $FILE;
944 945 946 947
	if ($git) {
		open($FILE, '-|', "git format-patch -M --stdout -1 $filename") ||
			die "$P: $filename: git format-patch failed - $!\n";
	} elsif ($file) {
948
		open($FILE, '-|', "diff -u /dev/null $filename") ||
949
			die "$P: $filename: diff failed - $!\n";
950 951
	} elsif ($filename eq '-') {
		open($FILE, '<&STDIN');
952
	} else {
953
		open($FILE, '<', "$filename") ||
954
			die "$P: $filename: open failed - $!\n";
955
	}
956 957
	if ($filename eq '-') {
		$vname = 'Your patch';
958
	} elsif ($git) {
959
		$vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")';
960 961 962
	} else {
		$vname = $filename;
	}
963
	while (<$FILE>) {
964 965 966
		chomp;
		push(@rawlines, $_);
	}
967
	close($FILE);
968 969 970 971 972 973 974

	if ($#ARGV > 0 && $quiet == 0) {
		print '-' x length($vname) . "\n";
		print "$vname\n";
		print '-' x length($vname) . "\n";
	}

975
	if (!process($filename)) {
976 977 978
		$exit = 1;
	}
	@rawlines = ();
979
	@lines = ();
980
	@fixed = ();
981 982
	@fixed_inserted = ();
	@fixed_deleted = ();
983
	$fixlinenr = -1;
984 985 986
	@modifierListFile = ();
	@typeListFile = ();
	build_types();
987 988
}

989
if (!$quiet) {
990 991 992
	hash_show_words(\%use_type, "Used");
	hash_show_words(\%ignore_type, "Ignored");

993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008
	if ($^V lt 5.10.0) {
		print << "EOM"

NOTE: perl $^V is not modern enough to detect all possible issues.
      An upgrade to at least perl v5.10.0 is suggested.
EOM
	}
	if ($exit) {
		print << "EOM"

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.
EOM
	}
}

1009 1010 1011
exit($exit);

sub top_of_kernel_tree {
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
	my ($root) = @_;

	my @tree_check = (
		"COPYING", "CREDITS", "Kbuild", "MAINTAINERS", "Makefile",
		"README", "Documentation", "arch", "include", "drivers",
		"fs", "init", "ipc", "kernel", "lib", "scripts",
	);

	foreach my $check (@tree_check) {
		if (! -e $root . '/' . $check) {
			return 0;
		}
1024
	}
1025
	return 1;
1026
}
1027

1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046
sub parse_email {
	my ($formatted_email) = @_;

	my $name = "";
	my $address = "";
	my $comment = "";

	if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) {
		$name = $1;
		$address = $2;
		$comment = $3 if defined $3;
	} elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) {
		$address = $1;
		$comment = $2 if defined $2;
	} elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) {
		$address = $1;
		$comment = $2 if defined $2;
		$formatted_email =~ s/$address.*$//;
		$name = $formatted_email;
1047
		$name = trim($name);
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
		$name =~ s/^\"|\"$//g;
		# If there's a name left after stripping spaces and
		# leading quotes, and the address doesn't have both
		# leading and trailing angle brackets, the address
		# is invalid. ie:
		#   "joe smith joe@smith.com" bad
		#   "joe smith <joe@smith.com" bad
		if ($name ne "" && $address !~ /^<[^>]+>$/) {
			$name = "";
			$address = "";
			$comment = "";
		}
	}

1062
	$name = trim($name);
1063
	$name =~ s/^\"|\"$//g;
1064
	$address = trim($address);
1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079
	$address =~ s/^\<|\>$//g;

	if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
		$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
		$name = "\"$name\"";
	}

	return ($name, $address, $comment);
}

sub format_email {
	my ($name, $address) = @_;

	my $formatted_email;

1080
	$name = trim($name);
1081
	$name =~ s/^\"|\"$//g;
1082
	$address = trim($address);
1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097

	if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
		$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
		$name = "\"$name\"";
	}

	if ("$name" eq "") {
		$formatted_email = "$address";
	} else {
		$formatted_email = "$name <$address>";
	}

	return $formatted_email;
}

1098
sub which {
1099
	my ($bin) = @_;
1100

1101 1102 1103 1104
	foreach my $path (split(/:/, $ENV{PATH})) {
		if (-e "$path/$bin") {
			return "$path/$bin";
		}
1105 1106
	}

1107
	return "";
1108 1109
}

1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
sub which_conf {
	my ($conf) = @_;

	foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
		if (-e "$path/$conf") {
			return "$path/$conf";
		}
	}

	return "";
}

1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
sub expand_tabs {
	my ($str) = @_;

	my $res = '';
	my $n = 0;
	for my $c (split(//, $str)) {
		if ($c eq "\t") {
			$res .= ' ';
			$n++;
			for (; ($n % 8) != 0; $n++) {
				$res .= ' ';
			}
			next;
		}
		$res .= $c;
		$n++;
	}

	return $res;
}
1142
sub copy_spacing {
1143
	(my $res = shift) =~ tr/\t/ /c;
1144 1145
	return $res;
}
1146

1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
sub line_stats {
	my ($line) = @_;

	# Drop the diff line leader and expand tabs
	$line =~ s/^.//;
	$line = expand_tabs($line);

	# Pick the indent from the front of the line.
	my ($white) = ($line =~ /^(\s*)/);

	return (length($line), length($white));
}

1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
my $sanitise_quote = '';

sub sanitise_line_reset {
	my ($in_comment) = @_;

	if ($in_comment) {
		$sanitise_quote = '*/';
	} else {
		$sanitise_quote = '';
	}
}
1171 1172 1173 1174 1175 1176
sub sanitise_line {
	my ($line) = @_;

	my $res = '';
	my $l = '';

1177
	my $qlen = 0;
1178 1179
	my $off = 0;
	my $c;
1180

1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194
	# Always copy over the diff marker.
	$res = substr($line, 0, 1);

	for ($off = 1; $off < length($line); $off++) {
		$c = substr($line, $off, 1);

		# Comments we are wacking completly including the begin
		# and end, all to $;.
		if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') {
			$sanitise_quote = '*/';

			substr($res, $off, 2, "$;$;");
			$off++;
			next;
1195
		}
1196
		if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') {
1197 1198 1199 1200
			$sanitise_quote = '';
			substr($res, $off, 2, "$;$;");
			$off++;
			next;
1201
		}
1202 1203 1204 1205 1206 1207 1208
		if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
			$sanitise_quote = '//';

			substr($res, $off, 2, $sanitise_quote);
			$off++;
			next;
		}
1209 1210 1211 1212 1213 1214 1215

		# A \ in a string means ignore the next character.
		if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
		    $c eq "\\") {
			substr($res, $off, 2, 'XX');
			$off++;
			next;
1216
		}
1217 1218 1219 1220
		# Regular quotes.
		if ($c eq "'" || $c eq '"') {
			if ($sanitise_quote eq '') {
				$sanitise_quote = $c;
1221

1222 1223 1224 1225 1226 1227
				substr($res, $off, 1, $c);
				next;
			} elsif ($sanitise_quote eq $c) {
				$sanitise_quote = '';
			}
		}
1228

1229
		#print "c<$c> SQ<$sanitise_quote>\n";
1230 1231
		if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
			substr($res, $off, 1, $;);
1232 1233
		} elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
			substr($res, $off, 1, $;);
1234 1235 1236 1237 1238
		} elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
			substr($res, $off, 1, 'X');
		} else {
			substr($res, $off, 1, $c);
		}
1239 1240
	}

1241 1242 1243 1244
	if ($sanitise_quote eq '//') {
		$sanitise_quote = '';
	}

1245
	# The pathname on a #include may be surrounded by '<' and '>'.
1246
	if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
1247 1248 1249 1250
		my $clean = 'X' x length($1);
		$res =~ s@\<.*\>@<$clean>@;

	# The whole of a #error is a string.
1251
	} elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) {
1252
		my $clean = 'X' x length($1);
1253
		$res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
1254 1255
	}

1256 1257 1258 1259 1260
	if ($allow_c99_comments && $res =~ m@(//.*$)@) {
		my $match = $1;
		$res =~ s/\Q$match\E/"$;" x length($match)/e;
	}

1261 1262 1263
	return $res;
}

1264 1265 1266
sub get_quoted_string {
	my ($line, $rawline) = @_;

1267
	return "" if ($line !~ m/($String)/g);
1268 1269 1270
	return substr($rawline, $-[0], $+[0] - $-[0]);
}

1271 1272 1273 1274 1275 1276
sub ctx_statement_block {
	my ($linenr, $remain, $off) = @_;
	my $line = $linenr - 1;
	my $blk = '';
	my $soff = $off;
	my $coff = $off - 1;
1277
	my $coff_set = 0;
1278

1279 1280
	my $loff = 0;

1281 1282
	my $type = '';
	my $level = 0;
1283
	my @stack = ();
1284
	my $p;
1285 1286
	my $c;
	my $len = 0;
1287 1288

	my $remainder;
1289
	while (1) {
1290 1291
		@stack = (['', 0]) if ($#stack == -1);

1292
		#warn "CSB: blk<$blk> remain<$remain>\n";
1293 1294 1295 1296
		# If we are about to drop off the end, pull in more
		# context.
		if ($off >= $len) {
			for (; $remain > 0; $line++) {
1297
				last if (!defined $lines[$line]);
1298
				next if ($lines[$line] =~ /^-/);
1299
				$remain--;
1300
				$loff = $len;
1301
				$blk .= $lines[$line] . "\n";
1302 1303 1304 1305 1306 1307
				$len = length($blk);
				$line++;
				last;
			}
			# Bail if there is no further context.
			#warn "CSB: blk<$blk> off<$off> len<$len>\n";
1308
			if ($off >= $len) {
1309 1310
				last;
			}
1311 1312 1313 1314
			if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) {
				$level++;
				$type = '#';
			}
1315
		}
1316
		$p = $c;
1317
		$c = substr($blk, $off, 1);
1318
		$remainder = substr($blk, $off);
1319

1320
		#warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n";
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330

		# Handle nested #if/#else.
		if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) {
			push(@stack, [ $type, $level ]);
		} elsif ($remainder =~ /^#\s*(?:else|elif)\b/) {
			($type, $level) = @{$stack[$#stack - 1]};
		} elsif ($remainder =~ /^#\s*endif\b/) {
			($type, $level) = @{pop(@stack)};
		}

1331 1332 1333 1334 1335 1336
		# Statement ends at the ';' or a close '}' at the
		# outermost level.
		if ($level == 0 && $c eq ';') {
			last;
		}

1337
		# An else is really a conditional as long as its not else if
1338 1339 1340 1341 1342 1343 1344 1345
		if ($level == 0 && $coff_set == 0 &&
				(!defined($p) || $p =~ /(?:\s|\}|\+)/) &&
				$remainder =~ /^(else)(?:\s|{)/ &&
				$remainder !~ /^else\s+if\b/) {
			$coff = $off + length($1) - 1;
			$coff_set = 1;
			#warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n";
			#warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n";
1346 1347
		}

1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
		if (($type eq '' || $type eq '(') && $c eq '(') {
			$level++;
			$type = '(';
		}
		if ($type eq '(' && $c eq ')') {
			$level--;
			$type = ($level != 0)? '(' : '';

			if ($level == 0 && $coff < $soff) {
				$coff = $off;
1358 1359
				$coff_set = 1;
				#warn "CSB: mark coff<$coff>\n";
1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370
			}
		}
		if (($type eq '' || $type eq '{') && $c eq '{') {
			$level++;
			$type = '{';
		}
		if ($type eq '{' && $c eq '}') {
			$level--;
			$type = ($level != 0)? '{' : '';

			if ($level == 0) {
1371 1372 1373
				if (substr($blk, $off + 1, 1) eq ';') {
					$off++;
				}
1374 1375 1376
				last;
			}
		}
1377 1378 1379 1380 1381 1382 1383
		# Preprocessor commands end at the newline unless escaped.
		if ($type eq '#' && $c eq "\n" && $p ne "\\") {
			$level--;
			$type = '';
			$off++;
			last;
		}
1384 1385
		$off++;
	}
1386
	# We are truly at the end, so shuffle to the next line.
1387
	if ($off == $len) {
1388
		$loff = $len + 1;
1389 1390 1391
		$line++;
		$remain--;
	}
1392 1393 1394 1395 1396 1397 1398

	my $statement = substr($blk, $soff, $off - $soff + 1);
	my $condition = substr($blk, $soff, $coff - $soff + 1);

	#warn "STATEMENT<$statement>\n";
	#warn "CONDITION<$condition>\n";

1399
	#print "coff<$coff> soff<$off> loff<$loff>\n";
1400 1401 1402 1403 1404

	return ($statement, $condition,
			$line, $remain + 1, $off - $loff + 1, $level);
}

1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
sub statement_lines {
	my ($stmt) = @_;

	# Strip the diff line prefixes and rip blank lines at start and end.
	$stmt =~ s/(^|\n)./$1/g;
	$stmt =~ s/^\s*//;
	$stmt =~ s/\s*$//;

	my @stmt_lines = ($stmt =~ /\n/g);

	return $#stmt_lines + 2;
}

sub statement_rawlines {
	my ($stmt) = @_;

	my @stmt_lines = ($stmt =~ /\n/g);

	return $#stmt_lines + 2;
}

sub statement_block_size {
	my ($stmt) = @_;

	$stmt =~ s/(^|\n)./$1/g;
	$stmt =~ s/^\s*{//;
	$stmt =~ s/}\s*$//;
	$stmt =~ s/^\s*//;
	$stmt =~ s/\s*$//;

	my @stmt_lines = ($stmt =~ /\n/g);
	my @stmt_statements = ($stmt =~ /;/g);

	my $stmt_lines = $#stmt_lines + 2;
	my $stmt_statements = $#stmt_statements + 1;

	if ($stmt_lines > $stmt_statements) {
		return $stmt_lines;
	} else {
		return $stmt_statements;
	}
}

1448 1449 1450 1451 1452 1453
sub ctx_statement_full {
	my ($linenr, $remain, $off) = @_;
	my ($statement, $condition, $level);

	my (@chunks);

1454
	# Grab the first conditional/block pair.