xref: /titanic_44/usr/src/tools/scripts/cstyle.pl (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate#!/usr/bin/perl -w
2*7c478bd9Sstevel@tonic-gate#
3*7c478bd9Sstevel@tonic-gate# CDDL HEADER START
4*7c478bd9Sstevel@tonic-gate#
5*7c478bd9Sstevel@tonic-gate# The contents of this file are subject to the terms of the
6*7c478bd9Sstevel@tonic-gate# Common Development and Distribution License, Version 1.0 only
7*7c478bd9Sstevel@tonic-gate# (the "License").  You may not use this file except in compliance
8*7c478bd9Sstevel@tonic-gate# with the License.
9*7c478bd9Sstevel@tonic-gate#
10*7c478bd9Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11*7c478bd9Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing.
12*7c478bd9Sstevel@tonic-gate# See the License for the specific language governing permissions
13*7c478bd9Sstevel@tonic-gate# and limitations under the License.
14*7c478bd9Sstevel@tonic-gate#
15*7c478bd9Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each
16*7c478bd9Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17*7c478bd9Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the
18*7c478bd9Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying
19*7c478bd9Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner]
20*7c478bd9Sstevel@tonic-gate#
21*7c478bd9Sstevel@tonic-gate# CDDL HEADER END
22*7c478bd9Sstevel@tonic-gate#
23*7c478bd9Sstevel@tonic-gate#
24*7c478bd9Sstevel@tonic-gate# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25*7c478bd9Sstevel@tonic-gate# Use is subject to license terms.
26*7c478bd9Sstevel@tonic-gate#
27*7c478bd9Sstevel@tonic-gate# @(#)cstyle 1.58 98/09/09 (from shannon)
28*7c478bd9Sstevel@tonic-gate#ident	"%Z%%M%	%I%	%E% SMI"
29*7c478bd9Sstevel@tonic-gate#
30*7c478bd9Sstevel@tonic-gate# cstyle - check for some common stylistic errors.
31*7c478bd9Sstevel@tonic-gate#
32*7c478bd9Sstevel@tonic-gate#	cstyle is a sort of "lint" for C coding style.
33*7c478bd9Sstevel@tonic-gate#	It attempts to check for the style used in the
34*7c478bd9Sstevel@tonic-gate#	kernel, sometimes known as "Bill Joy Normal Form".
35*7c478bd9Sstevel@tonic-gate#
36*7c478bd9Sstevel@tonic-gate#	There's a lot this can't check for, like proper indentation
37*7c478bd9Sstevel@tonic-gate#	of code blocks.  There's also a lot more this could check for.
38*7c478bd9Sstevel@tonic-gate#
39*7c478bd9Sstevel@tonic-gate#	A note to the non perl literate:
40*7c478bd9Sstevel@tonic-gate#
41*7c478bd9Sstevel@tonic-gate#		perl regular expressions are pretty much like egrep
42*7c478bd9Sstevel@tonic-gate#		regular expressions, with the following special symbols
43*7c478bd9Sstevel@tonic-gate#
44*7c478bd9Sstevel@tonic-gate#		\s	any space character
45*7c478bd9Sstevel@tonic-gate#		\S	any non-space character
46*7c478bd9Sstevel@tonic-gate#		\w	any "word" character [a-zA-Z0-9_]
47*7c478bd9Sstevel@tonic-gate#		\W	any non-word character
48*7c478bd9Sstevel@tonic-gate#		\d	a digit [0-9]
49*7c478bd9Sstevel@tonic-gate#		\D	a non-digit
50*7c478bd9Sstevel@tonic-gate#		\b	word boundary (between \w and \W)
51*7c478bd9Sstevel@tonic-gate#		\B	non-word boundary
52*7c478bd9Sstevel@tonic-gate#
53*7c478bd9Sstevel@tonic-gate
54*7c478bd9Sstevel@tonic-gaterequire 5.0;
55*7c478bd9Sstevel@tonic-gateuse IO::File;
56*7c478bd9Sstevel@tonic-gateuse Getopt::Std;
57*7c478bd9Sstevel@tonic-gateuse strict;
58*7c478bd9Sstevel@tonic-gate
59*7c478bd9Sstevel@tonic-gatemy $usage =
60*7c478bd9Sstevel@tonic-gate"usage: cstyle [-c] [-h] [-p] [-v] [-C] [-P] file ...
61*7c478bd9Sstevel@tonic-gate	-c	check continuation indentation inside functions
62*7c478bd9Sstevel@tonic-gate	-h	perform heuristic checks that are sometimes wrong
63*7c478bd9Sstevel@tonic-gate	-p	perform some of the more picky checks
64*7c478bd9Sstevel@tonic-gate	-v	verbose
65*7c478bd9Sstevel@tonic-gate	-C	don't check anything in header block comments
66*7c478bd9Sstevel@tonic-gate	-P	check for use of non-POSIX types
67*7c478bd9Sstevel@tonic-gate";
68*7c478bd9Sstevel@tonic-gate
69*7c478bd9Sstevel@tonic-gatemy %opts;
70*7c478bd9Sstevel@tonic-gate
71*7c478bd9Sstevel@tonic-gateif (!getopts("chpvCP", \%opts)) {
72*7c478bd9Sstevel@tonic-gate	print $usage;
73*7c478bd9Sstevel@tonic-gate	exit 1;
74*7c478bd9Sstevel@tonic-gate}
75*7c478bd9Sstevel@tonic-gate
76*7c478bd9Sstevel@tonic-gatemy $check_continuation = $opts{'c'};
77*7c478bd9Sstevel@tonic-gatemy $heuristic = $opts{'h'};
78*7c478bd9Sstevel@tonic-gatemy $picky = $opts{'p'};
79*7c478bd9Sstevel@tonic-gatemy $verbose = $opts{'v'};
80*7c478bd9Sstevel@tonic-gatemy $ignore_hdr_comment = $opts{'C'};
81*7c478bd9Sstevel@tonic-gatemy $check_posix_types = $opts{'P'};
82*7c478bd9Sstevel@tonic-gate
83*7c478bd9Sstevel@tonic-gatemy ($filename, $line, $prev);		# shared globals
84*7c478bd9Sstevel@tonic-gate
85*7c478bd9Sstevel@tonic-gatemy $fmt;
86*7c478bd9Sstevel@tonic-gate
87*7c478bd9Sstevel@tonic-gateif ($verbose) {
88*7c478bd9Sstevel@tonic-gate	$fmt = "%s: %d: %s\n%s\n";
89*7c478bd9Sstevel@tonic-gate} else {
90*7c478bd9Sstevel@tonic-gate	$fmt = "%s: %d: %s\n";
91*7c478bd9Sstevel@tonic-gate}
92*7c478bd9Sstevel@tonic-gate
93*7c478bd9Sstevel@tonic-gate# Note, following must be in single quotes so that \s and \w work right.
94*7c478bd9Sstevel@tonic-gatemy $typename = '(int|char|short|long|unsigned|float|double' .
95*7c478bd9Sstevel@tonic-gate    '|\w+_t|struct\s+\w+|union\s+\w+|FILE)';
96*7c478bd9Sstevel@tonic-gate
97*7c478bd9Sstevel@tonic-gate# mapping of old types to POSIX compatible types
98*7c478bd9Sstevel@tonic-gatemy %old2posix = (
99*7c478bd9Sstevel@tonic-gate	'unchar' => 'uchar_t',
100*7c478bd9Sstevel@tonic-gate	'ushort' => 'ushort_t',
101*7c478bd9Sstevel@tonic-gate	'uint' => 'uint_t',
102*7c478bd9Sstevel@tonic-gate	'ulong' => 'ulong_t',
103*7c478bd9Sstevel@tonic-gate	'u_int' => 'uint_t',
104*7c478bd9Sstevel@tonic-gate	'u_short' => 'ushort_t',
105*7c478bd9Sstevel@tonic-gate	'u_long' => 'ulong_t',
106*7c478bd9Sstevel@tonic-gate	'u_char' => 'uchar_t',
107*7c478bd9Sstevel@tonic-gate	'quad' => 'quad_t'
108*7c478bd9Sstevel@tonic-gate);
109*7c478bd9Sstevel@tonic-gate
110*7c478bd9Sstevel@tonic-gatemy $warlock_comment = "(
111*7c478bd9Sstevel@tonic-gateVARIABLES PROTECTED BY|
112*7c478bd9Sstevel@tonic-gateMEMBERS PROTECTED BY|
113*7c478bd9Sstevel@tonic-gateALL MEMBERS PROTECTED BY|
114*7c478bd9Sstevel@tonic-gateREAD-ONLY VARIABLES:|
115*7c478bd9Sstevel@tonic-gateREAD-ONLY MEMBERS:|
116*7c478bd9Sstevel@tonic-gateVARIABLES READABLE WITHOUT LOCK:|
117*7c478bd9Sstevel@tonic-gateMEMBERS READABLE WITHOUT LOCK:|
118*7c478bd9Sstevel@tonic-gateLOCKS COVERED BY|
119*7c478bd9Sstevel@tonic-gateLOCK UNNEEDED BECAUSE|
120*7c478bd9Sstevel@tonic-gateLOCK NEEDED:|
121*7c478bd9Sstevel@tonic-gateLOCK HELD ON ENTRY:|
122*7c478bd9Sstevel@tonic-gateREAD LOCK HELD ON ENTRY:|
123*7c478bd9Sstevel@tonic-gateWRITE LOCK HELD ON ENTRY:|
124*7c478bd9Sstevel@tonic-gateLOCK ACQUIRED AS SIDE EFFECT:|
125*7c478bd9Sstevel@tonic-gateREAD LOCK ACQUIRED AS SIDE EFFECT:|
126*7c478bd9Sstevel@tonic-gateWRITE LOCK ACQUIRED AS SIDE EFFECT:|
127*7c478bd9Sstevel@tonic-gateLOCK RELEASED AS SIDE EFFECT:|
128*7c478bd9Sstevel@tonic-gateLOCK UPGRADED AS SIDE EFFECT:|
129*7c478bd9Sstevel@tonic-gateLOCK DOWNGRADED AS SIDE EFFECT:|
130*7c478bd9Sstevel@tonic-gateFUNCTIONS CALLED THROUGH POINTER|
131*7c478bd9Sstevel@tonic-gateFUNCTIONS CALLED THROUGH MEMBER|
132*7c478bd9Sstevel@tonic-gateLOCK ORDER:
133*7c478bd9Sstevel@tonic-gate)";
134*7c478bd9Sstevel@tonic-gate$warlock_comment =~ tr/\n//d;
135*7c478bd9Sstevel@tonic-gate
136*7c478bd9Sstevel@tonic-gateif ($#ARGV >= 0) {
137*7c478bd9Sstevel@tonic-gate	foreach my $arg (@ARGV) {
138*7c478bd9Sstevel@tonic-gate		my $fh = new IO::File $arg, "r";
139*7c478bd9Sstevel@tonic-gate		if (!defined($fh)) {
140*7c478bd9Sstevel@tonic-gate			printf "%s: can not open\n", $arg;
141*7c478bd9Sstevel@tonic-gate		} else {
142*7c478bd9Sstevel@tonic-gate			&cstyle($arg, $fh);
143*7c478bd9Sstevel@tonic-gate			close $fh;
144*7c478bd9Sstevel@tonic-gate		}
145*7c478bd9Sstevel@tonic-gate	}
146*7c478bd9Sstevel@tonic-gate} else {
147*7c478bd9Sstevel@tonic-gate	&cstyle("<stdin>", *STDIN);
148*7c478bd9Sstevel@tonic-gate}
149*7c478bd9Sstevel@tonic-gate
150*7c478bd9Sstevel@tonic-gatemy $no_errs = 0;		# set for CSTYLED-protected lines
151*7c478bd9Sstevel@tonic-gate
152*7c478bd9Sstevel@tonic-gatesub err($) {
153*7c478bd9Sstevel@tonic-gate	my ($error) = @_;
154*7c478bd9Sstevel@tonic-gate	printf $fmt, $filename, $., $error, $line	unless ($no_errs);
155*7c478bd9Sstevel@tonic-gate}
156*7c478bd9Sstevel@tonic-gate
157*7c478bd9Sstevel@tonic-gatesub err_prefix($$) {
158*7c478bd9Sstevel@tonic-gate	my ($prevline, $error) = @_;
159*7c478bd9Sstevel@tonic-gate	my $out = $prevline."\n".$line;
160*7c478bd9Sstevel@tonic-gate	printf $fmt, $filename, $., $error, $out	unless ($no_errs);
161*7c478bd9Sstevel@tonic-gate}
162*7c478bd9Sstevel@tonic-gate
163*7c478bd9Sstevel@tonic-gatesub err_prev($) {
164*7c478bd9Sstevel@tonic-gate	my ($error) = @_;
165*7c478bd9Sstevel@tonic-gate	printf $fmt, $filename, $. - 1, $error, $prev	unless ($no_errs);
166*7c478bd9Sstevel@tonic-gate}
167*7c478bd9Sstevel@tonic-gate
168*7c478bd9Sstevel@tonic-gatesub cstyle($$) {
169*7c478bd9Sstevel@tonic-gate
170*7c478bd9Sstevel@tonic-gatemy ($fn, $filehandle) = @_;
171*7c478bd9Sstevel@tonic-gate$filename = $fn;			# share it globally
172*7c478bd9Sstevel@tonic-gate
173*7c478bd9Sstevel@tonic-gatemy $in_cpp = 0;
174*7c478bd9Sstevel@tonic-gatemy $next_in_cpp = 0;
175*7c478bd9Sstevel@tonic-gate
176*7c478bd9Sstevel@tonic-gatemy $in_comment = 0;
177*7c478bd9Sstevel@tonic-gatemy $in_header_comment = 0;
178*7c478bd9Sstevel@tonic-gatemy $comment_done = 0;
179*7c478bd9Sstevel@tonic-gatemy $in_warlock_comment = 0;
180*7c478bd9Sstevel@tonic-gatemy $in_function = 0;
181*7c478bd9Sstevel@tonic-gatemy $in_function_header = 0;
182*7c478bd9Sstevel@tonic-gatemy $in_declaration = 0;
183*7c478bd9Sstevel@tonic-gatemy $note_level = 0;
184*7c478bd9Sstevel@tonic-gatemy $nextok = 0;
185*7c478bd9Sstevel@tonic-gatemy $nocheck = 0;
186*7c478bd9Sstevel@tonic-gate
187*7c478bd9Sstevel@tonic-gatemy $in_string = 0;
188*7c478bd9Sstevel@tonic-gate
189*7c478bd9Sstevel@tonic-gatemy ($okmsg, $comment_prefix);
190*7c478bd9Sstevel@tonic-gate
191*7c478bd9Sstevel@tonic-gate$line = '';
192*7c478bd9Sstevel@tonic-gate$prev = '';
193*7c478bd9Sstevel@tonic-gatereset_indent();
194*7c478bd9Sstevel@tonic-gate
195*7c478bd9Sstevel@tonic-gateline: while (<$filehandle>) {
196*7c478bd9Sstevel@tonic-gate	s/\r?\n$//;	# strip return and newline
197*7c478bd9Sstevel@tonic-gate
198*7c478bd9Sstevel@tonic-gate	# save the original line, then remove all text from within
199*7c478bd9Sstevel@tonic-gate	# double or single quotes, we do not want to check such text.
200*7c478bd9Sstevel@tonic-gate
201*7c478bd9Sstevel@tonic-gate	$line = $_;
202*7c478bd9Sstevel@tonic-gate
203*7c478bd9Sstevel@tonic-gate	#
204*7c478bd9Sstevel@tonic-gate	# C allows strings to be continued with a backslash at the end of
205*7c478bd9Sstevel@tonic-gate	# the line.  We translate that into a quoted string on the previous
206*7c478bd9Sstevel@tonic-gate	# line followed by an initial quote on the next line.
207*7c478bd9Sstevel@tonic-gate	#
208*7c478bd9Sstevel@tonic-gate	# (we assume that no-one will use backslash-continuation with character
209*7c478bd9Sstevel@tonic-gate	# constants)
210*7c478bd9Sstevel@tonic-gate	#
211*7c478bd9Sstevel@tonic-gate	$_ = '"' . $_		if ($in_string && !$nocheck && !$in_comment);
212*7c478bd9Sstevel@tonic-gate
213*7c478bd9Sstevel@tonic-gate	#
214*7c478bd9Sstevel@tonic-gate	# normal strings and characters
215*7c478bd9Sstevel@tonic-gate	#
216*7c478bd9Sstevel@tonic-gate	s/'([^\\']|\\[^xX0]|\\0[0-9]*|\\[xX][0-9a-fA-F]*)'/''/g;
217*7c478bd9Sstevel@tonic-gate	s/"([^\\"]|\\.)*"/\"\"/g;
218*7c478bd9Sstevel@tonic-gate
219*7c478bd9Sstevel@tonic-gate	#
220*7c478bd9Sstevel@tonic-gate	# detect string continuation
221*7c478bd9Sstevel@tonic-gate	#
222*7c478bd9Sstevel@tonic-gate	if ($nocheck || $in_comment) {
223*7c478bd9Sstevel@tonic-gate		$in_string = 0;
224*7c478bd9Sstevel@tonic-gate	} else {
225*7c478bd9Sstevel@tonic-gate		#
226*7c478bd9Sstevel@tonic-gate		# Now that all full strings are replaced with "", we check
227*7c478bd9Sstevel@tonic-gate		# for unfinished strings continuing onto the next line.
228*7c478bd9Sstevel@tonic-gate		#
229*7c478bd9Sstevel@tonic-gate		$in_string =
230*7c478bd9Sstevel@tonic-gate		    (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ ||
231*7c478bd9Sstevel@tonic-gate		    s/^("")*"([^\\"]|\\.)*\\$/""/);
232*7c478bd9Sstevel@tonic-gate	}
233*7c478bd9Sstevel@tonic-gate
234*7c478bd9Sstevel@tonic-gate	#
235*7c478bd9Sstevel@tonic-gate	# figure out if we are in a cpp directive
236*7c478bd9Sstevel@tonic-gate	#
237*7c478bd9Sstevel@tonic-gate	$in_cpp = $next_in_cpp || /^\s*#/;	# continued or started
238*7c478bd9Sstevel@tonic-gate	$next_in_cpp = $in_cpp && /\\$/;	# only if continued
239*7c478bd9Sstevel@tonic-gate
240*7c478bd9Sstevel@tonic-gate	# strip off trailing backslashes, which appear in long macros
241*7c478bd9Sstevel@tonic-gate	s/\s*\\$//;
242*7c478bd9Sstevel@tonic-gate
243*7c478bd9Sstevel@tonic-gate	# an /* END CSTYLED */ comment ends a no-check block.
244*7c478bd9Sstevel@tonic-gate	if ($nocheck) {
245*7c478bd9Sstevel@tonic-gate		if (/\/\* *END *CSTYLED *\*\//) {
246*7c478bd9Sstevel@tonic-gate			$nocheck = 0;
247*7c478bd9Sstevel@tonic-gate		} else {
248*7c478bd9Sstevel@tonic-gate			reset_indent();
249*7c478bd9Sstevel@tonic-gate			next line;
250*7c478bd9Sstevel@tonic-gate		}
251*7c478bd9Sstevel@tonic-gate	}
252*7c478bd9Sstevel@tonic-gate
253*7c478bd9Sstevel@tonic-gate	# a /*CSTYLED*/ comment indicates that the next line is ok.
254*7c478bd9Sstevel@tonic-gate	if ($nextok) {
255*7c478bd9Sstevel@tonic-gate		if ($okmsg) {
256*7c478bd9Sstevel@tonic-gate			err($okmsg);
257*7c478bd9Sstevel@tonic-gate		}
258*7c478bd9Sstevel@tonic-gate		$nextok = 0;
259*7c478bd9Sstevel@tonic-gate		$okmsg = 0;
260*7c478bd9Sstevel@tonic-gate		if (/\/\* *CSTYLED.*\*\//) {
261*7c478bd9Sstevel@tonic-gate			/^.*\/\* *CSTYLED *(.*) *\*\/.*$/;
262*7c478bd9Sstevel@tonic-gate			$okmsg = $1;
263*7c478bd9Sstevel@tonic-gate			$nextok = 1;
264*7c478bd9Sstevel@tonic-gate		}
265*7c478bd9Sstevel@tonic-gate		$no_errs = 1;
266*7c478bd9Sstevel@tonic-gate	} elsif ($no_errs) {
267*7c478bd9Sstevel@tonic-gate		$no_errs = 0;
268*7c478bd9Sstevel@tonic-gate	}
269*7c478bd9Sstevel@tonic-gate
270*7c478bd9Sstevel@tonic-gate	# check length of line.
271*7c478bd9Sstevel@tonic-gate	# first, a quick check to see if there is any chance of being too long.
272*7c478bd9Sstevel@tonic-gate	if (($line =~ tr/\t/\t/) * 7 + length($line) > 80) {
273*7c478bd9Sstevel@tonic-gate		# yes, there is a chance.
274*7c478bd9Sstevel@tonic-gate		# replace tabs with spaces and check again.
275*7c478bd9Sstevel@tonic-gate		my $eline = $line;
276*7c478bd9Sstevel@tonic-gate		1 while $eline =~
277*7c478bd9Sstevel@tonic-gate		    s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
278*7c478bd9Sstevel@tonic-gate		if (length($eline) > 80) {
279*7c478bd9Sstevel@tonic-gate			err("line > 80 characters");
280*7c478bd9Sstevel@tonic-gate		}
281*7c478bd9Sstevel@tonic-gate	}
282*7c478bd9Sstevel@tonic-gate
283*7c478bd9Sstevel@tonic-gate	# ignore NOTE(...) annotations (assumes NOTE is on lines by itself).
284*7c478bd9Sstevel@tonic-gate	if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE
285*7c478bd9Sstevel@tonic-gate		s/[^()]//g;			  # eliminate all non-parens
286*7c478bd9Sstevel@tonic-gate		$note_level += s/\(//g - length;  # update paren nest level
287*7c478bd9Sstevel@tonic-gate		next;
288*7c478bd9Sstevel@tonic-gate	}
289*7c478bd9Sstevel@tonic-gate
290*7c478bd9Sstevel@tonic-gate	# a /* BEGIN CSTYLED */ comment starts a no-check block.
291*7c478bd9Sstevel@tonic-gate	if (/\/\* *BEGIN *CSTYLED *\*\//) {
292*7c478bd9Sstevel@tonic-gate		$nocheck = 1;
293*7c478bd9Sstevel@tonic-gate	}
294*7c478bd9Sstevel@tonic-gate
295*7c478bd9Sstevel@tonic-gate	# a /*CSTYLED*/ comment indicates that the next line is ok.
296*7c478bd9Sstevel@tonic-gate	if (/\/\* *CSTYLED.*\*\//) {
297*7c478bd9Sstevel@tonic-gate		/^.*\/\* *CSTYLED *(.*) *\*\/.*$/;
298*7c478bd9Sstevel@tonic-gate		$okmsg = $1;
299*7c478bd9Sstevel@tonic-gate		$nextok = 1;
300*7c478bd9Sstevel@tonic-gate	}
301*7c478bd9Sstevel@tonic-gate	if (/\/\/ *CSTYLED/) {
302*7c478bd9Sstevel@tonic-gate		/^.*\/\/ *CSTYLED *(.*)$/;
303*7c478bd9Sstevel@tonic-gate		$okmsg = $1;
304*7c478bd9Sstevel@tonic-gate		$nextok = 1;
305*7c478bd9Sstevel@tonic-gate	}
306*7c478bd9Sstevel@tonic-gate
307*7c478bd9Sstevel@tonic-gate	# universal checks; apply to everything
308*7c478bd9Sstevel@tonic-gate	if (/\t +\t/) {
309*7c478bd9Sstevel@tonic-gate		err("spaces between tabs");
310*7c478bd9Sstevel@tonic-gate	}
311*7c478bd9Sstevel@tonic-gate	if (/ \t+ /) {
312*7c478bd9Sstevel@tonic-gate		err("tabs between spaces");
313*7c478bd9Sstevel@tonic-gate	}
314*7c478bd9Sstevel@tonic-gate	if (/\s$/) {
315*7c478bd9Sstevel@tonic-gate		err("space or tab at end of line");
316*7c478bd9Sstevel@tonic-gate	}
317*7c478bd9Sstevel@tonic-gate	if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) {
318*7c478bd9Sstevel@tonic-gate		err("comment preceded by non-blank");
319*7c478bd9Sstevel@tonic-gate	}
320*7c478bd9Sstevel@tonic-gate
321*7c478bd9Sstevel@tonic-gate	# is this the beginning or ending of a function?
322*7c478bd9Sstevel@tonic-gate	# (not if "struct foo\n{\n")
323*7c478bd9Sstevel@tonic-gate	if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) {
324*7c478bd9Sstevel@tonic-gate		$in_function = 1;
325*7c478bd9Sstevel@tonic-gate		$in_declaration = 1;
326*7c478bd9Sstevel@tonic-gate		$in_function_header = 0;
327*7c478bd9Sstevel@tonic-gate		$prev = $line;
328*7c478bd9Sstevel@tonic-gate		next line;
329*7c478bd9Sstevel@tonic-gate	}
330*7c478bd9Sstevel@tonic-gate	if (/^}\s*(\/\*.*\*\/\s*)*$/) {
331*7c478bd9Sstevel@tonic-gate		if ($prev =~ /^\s*return\s*;/) {
332*7c478bd9Sstevel@tonic-gate			err_prev("unneeded return at end of function");
333*7c478bd9Sstevel@tonic-gate		}
334*7c478bd9Sstevel@tonic-gate		$in_function = 0;
335*7c478bd9Sstevel@tonic-gate		reset_indent();		# we don't check between functions
336*7c478bd9Sstevel@tonic-gate		$prev = $line;
337*7c478bd9Sstevel@tonic-gate		next line;
338*7c478bd9Sstevel@tonic-gate	}
339*7c478bd9Sstevel@tonic-gate	if (/^\w*\($/) {
340*7c478bd9Sstevel@tonic-gate		$in_function_header = 1;
341*7c478bd9Sstevel@tonic-gate	}
342*7c478bd9Sstevel@tonic-gate
343*7c478bd9Sstevel@tonic-gate	if ($in_warlock_comment && /\*\//) {
344*7c478bd9Sstevel@tonic-gate		$in_warlock_comment = 0;
345*7c478bd9Sstevel@tonic-gate		$prev = $line;
346*7c478bd9Sstevel@tonic-gate		next line;
347*7c478bd9Sstevel@tonic-gate	}
348*7c478bd9Sstevel@tonic-gate
349*7c478bd9Sstevel@tonic-gate	# a blank line terminates the declarations within a function.
350*7c478bd9Sstevel@tonic-gate	# XXX - but still a problem in sub-blocks.
351*7c478bd9Sstevel@tonic-gate	if ($in_declaration && /^$/) {
352*7c478bd9Sstevel@tonic-gate		$in_declaration = 0;
353*7c478bd9Sstevel@tonic-gate	}
354*7c478bd9Sstevel@tonic-gate
355*7c478bd9Sstevel@tonic-gate	if ($comment_done) {
356*7c478bd9Sstevel@tonic-gate		$in_comment = 0;
357*7c478bd9Sstevel@tonic-gate		$in_header_comment = 0;
358*7c478bd9Sstevel@tonic-gate		$comment_done = 0;
359*7c478bd9Sstevel@tonic-gate	}
360*7c478bd9Sstevel@tonic-gate	# does this looks like the start of a block comment?
361*7c478bd9Sstevel@tonic-gate	if (/^\s*\/\*$/) {
362*7c478bd9Sstevel@tonic-gate		if (!/^\t*\/\*$/) {
363*7c478bd9Sstevel@tonic-gate			err("block comment not indented by tabs");
364*7c478bd9Sstevel@tonic-gate		}
365*7c478bd9Sstevel@tonic-gate		$in_comment = 1;
366*7c478bd9Sstevel@tonic-gate		s/\/\*//;
367*7c478bd9Sstevel@tonic-gate		$comment_prefix = $_;
368*7c478bd9Sstevel@tonic-gate		if ($comment_prefix eq "") {
369*7c478bd9Sstevel@tonic-gate			$in_header_comment = 1;
370*7c478bd9Sstevel@tonic-gate		}
371*7c478bd9Sstevel@tonic-gate		$prev = $line;
372*7c478bd9Sstevel@tonic-gate		next line;
373*7c478bd9Sstevel@tonic-gate	}
374*7c478bd9Sstevel@tonic-gate	# are we still in the block comment?
375*7c478bd9Sstevel@tonic-gate	if ($in_comment) {
376*7c478bd9Sstevel@tonic-gate		if (/^$comment_prefix \*\/$/) {
377*7c478bd9Sstevel@tonic-gate			$comment_done = 1;
378*7c478bd9Sstevel@tonic-gate		} elsif (/\*\//) {
379*7c478bd9Sstevel@tonic-gate			$comment_done = 1;
380*7c478bd9Sstevel@tonic-gate			err("improper block comment close")
381*7c478bd9Sstevel@tonic-gate			    unless ($ignore_hdr_comment && $in_header_comment);
382*7c478bd9Sstevel@tonic-gate		} elsif (!/^$comment_prefix \*[ \t]/ &&
383*7c478bd9Sstevel@tonic-gate		    !/^$comment_prefix \*$/) {
384*7c478bd9Sstevel@tonic-gate			err("improper block comment")
385*7c478bd9Sstevel@tonic-gate			    unless ($ignore_hdr_comment && $in_header_comment);
386*7c478bd9Sstevel@tonic-gate		}
387*7c478bd9Sstevel@tonic-gate	}
388*7c478bd9Sstevel@tonic-gate
389*7c478bd9Sstevel@tonic-gate	if ($in_header_comment && $ignore_hdr_comment) {
390*7c478bd9Sstevel@tonic-gate		$prev = $line;
391*7c478bd9Sstevel@tonic-gate		next line;
392*7c478bd9Sstevel@tonic-gate	}
393*7c478bd9Sstevel@tonic-gate
394*7c478bd9Sstevel@tonic-gate	# check for errors that might occur in comments and in code.
395*7c478bd9Sstevel@tonic-gate
396*7c478bd9Sstevel@tonic-gate	# allow spaces to be used to draw pictures in header comments.
397*7c478bd9Sstevel@tonic-gate	if (/[^ ]     / && !/".*     .*"/ && !$in_header_comment) {
398*7c478bd9Sstevel@tonic-gate		err("spaces instead of tabs");
399*7c478bd9Sstevel@tonic-gate	}
400*7c478bd9Sstevel@tonic-gate	if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ &&
401*7c478bd9Sstevel@tonic-gate	    (!/^    \w/ || $in_function != 0)) {
402*7c478bd9Sstevel@tonic-gate		err("indent by spaces instead of tabs");
403*7c478bd9Sstevel@tonic-gate	}
404*7c478bd9Sstevel@tonic-gate	if (/^\t+ [^ \t\*]/ || /^\t+  \S/ || /^\t+   \S/) {
405*7c478bd9Sstevel@tonic-gate		err("continuation line not indented by 4 spaces");
406*7c478bd9Sstevel@tonic-gate	}
407*7c478bd9Sstevel@tonic-gate	if (/\/\*\s*$warlock_comment/o && !/\*\//) {
408*7c478bd9Sstevel@tonic-gate		$in_warlock_comment = 1;
409*7c478bd9Sstevel@tonic-gate		$prev = $line;
410*7c478bd9Sstevel@tonic-gate		next line;
411*7c478bd9Sstevel@tonic-gate	}
412*7c478bd9Sstevel@tonic-gate	if (/^\s*\/\*./ && !/^\s*\/\*.*\*\//) {
413*7c478bd9Sstevel@tonic-gate		err("improper first line of block comment");
414*7c478bd9Sstevel@tonic-gate	}
415*7c478bd9Sstevel@tonic-gate
416*7c478bd9Sstevel@tonic-gate	if ($in_comment) {	# still in comment, don't do further checks
417*7c478bd9Sstevel@tonic-gate		$prev = $line;
418*7c478bd9Sstevel@tonic-gate		next line;
419*7c478bd9Sstevel@tonic-gate	}
420*7c478bd9Sstevel@tonic-gate
421*7c478bd9Sstevel@tonic-gate	if ((/[^(]\/\*\S/ || /^\/\*\S/) &&
422*7c478bd9Sstevel@tonic-gate	    !(/\/\*(ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*)\*\// ||
423*7c478bd9Sstevel@tonic-gate	    /\/\*(CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY)\*\// ||
424*7c478bd9Sstevel@tonic-gate	    /\/\*(FALLTHRU|FALLTHROUGH|LINTED.*|PRINTFLIKE[0-9]*)\*\// ||
425*7c478bd9Sstevel@tonic-gate	    /\/\*(PROTOLIB[0-9]*|SCANFLIKE[0-9]*|CSTYLED.*)\*\//)) {
426*7c478bd9Sstevel@tonic-gate		err("missing blank after open comment");
427*7c478bd9Sstevel@tonic-gate	}
428*7c478bd9Sstevel@tonic-gate	if (/\S\*\/[^)]|\S\*\/$/ &&
429*7c478bd9Sstevel@tonic-gate	    !(/\/\*(ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*)\*\// ||
430*7c478bd9Sstevel@tonic-gate	    /\/\*(CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY)\*\// ||
431*7c478bd9Sstevel@tonic-gate	    /\/\*(FALLTHRU|FALLTHROUGH|LINTED.*|PRINTFLIKE[0-9]*)\*\// ||
432*7c478bd9Sstevel@tonic-gate	    /\/\*(PROTOLIB[0-9]*|SCANFLIKE[0-9]*|CSTYLED.*)\*\//)) {
433*7c478bd9Sstevel@tonic-gate		err("missing blank before close comment");
434*7c478bd9Sstevel@tonic-gate	}
435*7c478bd9Sstevel@tonic-gate	if (/\/\/\S/) {		# C++ comments
436*7c478bd9Sstevel@tonic-gate		err("missing blank after start comment");
437*7c478bd9Sstevel@tonic-gate	}
438*7c478bd9Sstevel@tonic-gate	# check for unterminated single line comments, but allow them when
439*7c478bd9Sstevel@tonic-gate	# they are used to comment out the argument list of a function
440*7c478bd9Sstevel@tonic-gate	# declaration.
441*7c478bd9Sstevel@tonic-gate	if (/\S.*\/\*/ && !/\S.*\/\*.*\*\// && !/\(\/\*/) {
442*7c478bd9Sstevel@tonic-gate		err("unterminated single line comment");
443*7c478bd9Sstevel@tonic-gate	}
444*7c478bd9Sstevel@tonic-gate
445*7c478bd9Sstevel@tonic-gate	if (/^(#else|#endif|#include)(.*)$/) {
446*7c478bd9Sstevel@tonic-gate		$prev = $line;
447*7c478bd9Sstevel@tonic-gate		if ($picky) {
448*7c478bd9Sstevel@tonic-gate			my $directive = $1;
449*7c478bd9Sstevel@tonic-gate			my $clause = $2;
450*7c478bd9Sstevel@tonic-gate			# Enforce ANSI rules for #else and #endif: no noncomment
451*7c478bd9Sstevel@tonic-gate			# identifiers are allowed after #endif or #else.  Allow
452*7c478bd9Sstevel@tonic-gate			# C++ comments since they seem to be a fact of life.
453*7c478bd9Sstevel@tonic-gate			if ((($1 eq "#endif") || ($1 eq "#else")) &&
454*7c478bd9Sstevel@tonic-gate			    ($clause ne "") &&
455*7c478bd9Sstevel@tonic-gate			    (!($clause =~ /^\s+\/\*.*\*\/$/)) &&
456*7c478bd9Sstevel@tonic-gate			    (!($clause =~ /^\s+\/\/.*$/))) {
457*7c478bd9Sstevel@tonic-gate				err("non-comment text following " .
458*7c478bd9Sstevel@tonic-gate				    "$directive (or malformed $directive " .
459*7c478bd9Sstevel@tonic-gate				    "directive)");
460*7c478bd9Sstevel@tonic-gate			}
461*7c478bd9Sstevel@tonic-gate		}
462*7c478bd9Sstevel@tonic-gate		next line;
463*7c478bd9Sstevel@tonic-gate	}
464*7c478bd9Sstevel@tonic-gate
465*7c478bd9Sstevel@tonic-gate	#
466*7c478bd9Sstevel@tonic-gate	# delete any comments and check everything else.  Note that
467*7c478bd9Sstevel@tonic-gate	# ".*?" is a non-greedy match, so that we don't get confused by
468*7c478bd9Sstevel@tonic-gate	# multiple comments on the same line.
469*7c478bd9Sstevel@tonic-gate	#
470*7c478bd9Sstevel@tonic-gate	s/\/\*.*?\*\///g;
471*7c478bd9Sstevel@tonic-gate	s/\/\/.*$//;		# C++ comments
472*7c478bd9Sstevel@tonic-gate
473*7c478bd9Sstevel@tonic-gate	# delete any trailing whitespace; we have already checked for that.
474*7c478bd9Sstevel@tonic-gate	s/\s*$//;
475*7c478bd9Sstevel@tonic-gate
476*7c478bd9Sstevel@tonic-gate	# following checks do not apply to text in comments.
477*7c478bd9Sstevel@tonic-gate
478*7c478bd9Sstevel@tonic-gate	if (/[^<>\s][!<>=]=/ || /[^<>][!<>=]=[^\s,]/ ||
479*7c478bd9Sstevel@tonic-gate	    (/[^->]>[^,=>\s]/ && !/[^->]>$/) ||
480*7c478bd9Sstevel@tonic-gate	    (/[^<]<[^,=<\s]/ && !/[^<]<$/) ||
481*7c478bd9Sstevel@tonic-gate	    /[^<\s]<[^<]/ || /[^->\s]>[^>]/) {
482*7c478bd9Sstevel@tonic-gate		err("missing space around relational operator");
483*7c478bd9Sstevel@tonic-gate	}
484*7c478bd9Sstevel@tonic-gate	if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ ||
485*7c478bd9Sstevel@tonic-gate	    (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) ||
486*7c478bd9Sstevel@tonic-gate	    (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) {
487*7c478bd9Sstevel@tonic-gate		# XXX - should only check this for C++ code
488*7c478bd9Sstevel@tonic-gate		# XXX - there are probably other forms that should be allowed
489*7c478bd9Sstevel@tonic-gate		if (!/\soperator=/) {
490*7c478bd9Sstevel@tonic-gate			err("missing space around assignment operator");
491*7c478bd9Sstevel@tonic-gate		}
492*7c478bd9Sstevel@tonic-gate	}
493*7c478bd9Sstevel@tonic-gate	if (/[,;]\S/ && !/\bfor \(;;\)/) {
494*7c478bd9Sstevel@tonic-gate		err("comma or semicolon followed by non-blank");
495*7c478bd9Sstevel@tonic-gate	}
496*7c478bd9Sstevel@tonic-gate	# allow "for" statements to have empty "while" clauses
497*7c478bd9Sstevel@tonic-gate	if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) {
498*7c478bd9Sstevel@tonic-gate		err("comma or semicolon preceded by blank");
499*7c478bd9Sstevel@tonic-gate	}
500*7c478bd9Sstevel@tonic-gate	if (/^\s*(&&|\|\|)/) {
501*7c478bd9Sstevel@tonic-gate		err("improper boolean continuation");
502*7c478bd9Sstevel@tonic-gate	}
503*7c478bd9Sstevel@tonic-gate	if (/\S   *(&&|\|\|)/ || /(&&|\|\|)   *\S/) {
504*7c478bd9Sstevel@tonic-gate		err("more than one space around boolean operator");
505*7c478bd9Sstevel@tonic-gate	}
506*7c478bd9Sstevel@tonic-gate	if (/\b(for|if|while|switch|sizeof|return|case)\(/) {
507*7c478bd9Sstevel@tonic-gate		err("missing space between keyword and paren");
508*7c478bd9Sstevel@tonic-gate	}
509*7c478bd9Sstevel@tonic-gate	if (/(\b(for|if|while|switch|return)\b.*){2,}/ && !/^#define/) {
510*7c478bd9Sstevel@tonic-gate		# multiple "case" and "sizeof" allowed
511*7c478bd9Sstevel@tonic-gate		err("more than one keyword on line");
512*7c478bd9Sstevel@tonic-gate	}
513*7c478bd9Sstevel@tonic-gate	if (/\b(for|if|while|switch|sizeof|return|case)\s\s+\(/ &&
514*7c478bd9Sstevel@tonic-gate	    !/^#if\s+\(/) {
515*7c478bd9Sstevel@tonic-gate		err("extra space between keyword and paren");
516*7c478bd9Sstevel@tonic-gate	}
517*7c478bd9Sstevel@tonic-gate	# try to detect "func (x)" but not "if (x)" or
518*7c478bd9Sstevel@tonic-gate	# "#define foo (x)" or "int (*func)();"
519*7c478bd9Sstevel@tonic-gate	if (/\w\s\(/) {
520*7c478bd9Sstevel@tonic-gate		my $s = $_;
521*7c478bd9Sstevel@tonic-gate		# strip off all keywords on the line
522*7c478bd9Sstevel@tonic-gate		s/\b(for|if|while|switch|return|case|sizeof)\s\(/XXX(/g;
523*7c478bd9Sstevel@tonic-gate		s/#elif\s\(/XXX(/g;
524*7c478bd9Sstevel@tonic-gate		s/^#define\s+\w+\s+\(/XXX(/;
525*7c478bd9Sstevel@tonic-gate		# do not match things like "void (*f)();"
526*7c478bd9Sstevel@tonic-gate		# or "typedef void (func_t)();"
527*7c478bd9Sstevel@tonic-gate		s/\w\s\(+\*/XXX(*/g;
528*7c478bd9Sstevel@tonic-gate		s/\b($typename|void)\s+\(+/XXX(/og;
529*7c478bd9Sstevel@tonic-gate		if (/\w\s\(/) {
530*7c478bd9Sstevel@tonic-gate			err("extra space between function name and left paren");
531*7c478bd9Sstevel@tonic-gate		}
532*7c478bd9Sstevel@tonic-gate		$_ = $s;
533*7c478bd9Sstevel@tonic-gate	}
534*7c478bd9Sstevel@tonic-gate	# try to detect "int foo(x)", but not "extern int foo(x);"
535*7c478bd9Sstevel@tonic-gate	# XXX - this still trips over too many legitimate things,
536*7c478bd9Sstevel@tonic-gate	# like "int foo(x,\n\ty);"
537*7c478bd9Sstevel@tonic-gate#		if (/^(\w+(\s|\*)+)+\w+\(/ && !/\)[;,](\s|)*$/ &&
538*7c478bd9Sstevel@tonic-gate#		    !/^(extern|static)\b/) {
539*7c478bd9Sstevel@tonic-gate#			err("return type of function not on separate line");
540*7c478bd9Sstevel@tonic-gate#		}
541*7c478bd9Sstevel@tonic-gate	# this is a close approximation
542*7c478bd9Sstevel@tonic-gate	if (/^(\w+(\s|\*)+)+\w+\(.*\)(\s|)*$/ &&
543*7c478bd9Sstevel@tonic-gate	    !/^(extern|static)\b/) {
544*7c478bd9Sstevel@tonic-gate		err("return type of function not on separate line");
545*7c478bd9Sstevel@tonic-gate	}
546*7c478bd9Sstevel@tonic-gate	if (/^#define /) {
547*7c478bd9Sstevel@tonic-gate		err("#define followed by space instead of tab");
548*7c478bd9Sstevel@tonic-gate	}
549*7c478bd9Sstevel@tonic-gate	if (/^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) {
550*7c478bd9Sstevel@tonic-gate		err("unparenthesized return expression");
551*7c478bd9Sstevel@tonic-gate	}
552*7c478bd9Sstevel@tonic-gate	if (/\bsizeof\b/ && !/\bsizeof\s*\(.*\)/) {
553*7c478bd9Sstevel@tonic-gate		err("unparenthesized sizeof expression");
554*7c478bd9Sstevel@tonic-gate	}
555*7c478bd9Sstevel@tonic-gate	if (/\(\s/) {
556*7c478bd9Sstevel@tonic-gate		err("whitespace after left paren");
557*7c478bd9Sstevel@tonic-gate	}
558*7c478bd9Sstevel@tonic-gate	# allow "for" statements to have empty "continue" clauses
559*7c478bd9Sstevel@tonic-gate	if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) {
560*7c478bd9Sstevel@tonic-gate		err("whitespace before right paren");
561*7c478bd9Sstevel@tonic-gate	}
562*7c478bd9Sstevel@tonic-gate	if (/^\s*\(void\)[^ ]/) {
563*7c478bd9Sstevel@tonic-gate		err("missing space after (void) cast");
564*7c478bd9Sstevel@tonic-gate	}
565*7c478bd9Sstevel@tonic-gate	if (/\S{/ && !/{{/) {
566*7c478bd9Sstevel@tonic-gate		err("missing space before left brace");
567*7c478bd9Sstevel@tonic-gate	}
568*7c478bd9Sstevel@tonic-gate	if ($in_function && /^\s+{/ &&
569*7c478bd9Sstevel@tonic-gate	    ($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) {
570*7c478bd9Sstevel@tonic-gate		err("left brace starting a line");
571*7c478bd9Sstevel@tonic-gate	}
572*7c478bd9Sstevel@tonic-gate	if (/}(else|while)/) {
573*7c478bd9Sstevel@tonic-gate		err("missing space after right brace");
574*7c478bd9Sstevel@tonic-gate	}
575*7c478bd9Sstevel@tonic-gate	if (/}\s\s+(else|while)/) {
576*7c478bd9Sstevel@tonic-gate		err("extra space after right brace");
577*7c478bd9Sstevel@tonic-gate	}
578*7c478bd9Sstevel@tonic-gate	if (/\b_VOID\b|\bVOID\b|\bSTATIC\b/) {
579*7c478bd9Sstevel@tonic-gate		err("obsolete use of VOID or STATIC");
580*7c478bd9Sstevel@tonic-gate	}
581*7c478bd9Sstevel@tonic-gate	if (/\b$typename\*/o) {
582*7c478bd9Sstevel@tonic-gate		err("missing space between type name and *");
583*7c478bd9Sstevel@tonic-gate	}
584*7c478bd9Sstevel@tonic-gate	if (/^\s+#/) {
585*7c478bd9Sstevel@tonic-gate		err("preprocessor statement not in column 1");
586*7c478bd9Sstevel@tonic-gate	}
587*7c478bd9Sstevel@tonic-gate	if (/^#\s/) {
588*7c478bd9Sstevel@tonic-gate		err("blank after preprocessor #");
589*7c478bd9Sstevel@tonic-gate	}
590*7c478bd9Sstevel@tonic-gate	if (/!\s*(strcmp|strncmp|bcmp)\s*\(/) {
591*7c478bd9Sstevel@tonic-gate		err("don't use boolean ! with comparison functions");
592*7c478bd9Sstevel@tonic-gate	}
593*7c478bd9Sstevel@tonic-gate
594*7c478bd9Sstevel@tonic-gate	#
595*7c478bd9Sstevel@tonic-gate	# We completely ignore, for purposes of indentation:
596*7c478bd9Sstevel@tonic-gate	#  * lines outside of functions
597*7c478bd9Sstevel@tonic-gate	#  * preprocessor lines
598*7c478bd9Sstevel@tonic-gate	#
599*7c478bd9Sstevel@tonic-gate	if ($check_continuation && $in_function && !$in_cpp) {
600*7c478bd9Sstevel@tonic-gate		process_indent($_);
601*7c478bd9Sstevel@tonic-gate	}
602*7c478bd9Sstevel@tonic-gate	if ($picky) {
603*7c478bd9Sstevel@tonic-gate		# try to detect spaces after casts, but allow (e.g.)
604*7c478bd9Sstevel@tonic-gate		# "sizeof (int) + 1" and "void (*funcptr)(int) = foo;"
605*7c478bd9Sstevel@tonic-gate		if (/\($typename( \*+)?\)\s/o &&
606*7c478bd9Sstevel@tonic-gate		    !/sizeof\s*\($typename( \*)?\)\s/o &&
607*7c478bd9Sstevel@tonic-gate		    !/\($typename( \*+)?\)\s+=[^=]/o) {
608*7c478bd9Sstevel@tonic-gate			err("space after cast");
609*7c478bd9Sstevel@tonic-gate		}
610*7c478bd9Sstevel@tonic-gate		if (/\b$typename\s*\*\s/o &&
611*7c478bd9Sstevel@tonic-gate		    !/\b$typename\s*\*\s+const\b/o) {
612*7c478bd9Sstevel@tonic-gate			err("unary * followed by space");
613*7c478bd9Sstevel@tonic-gate		}
614*7c478bd9Sstevel@tonic-gate	}
615*7c478bd9Sstevel@tonic-gate	if ($check_posix_types) {
616*7c478bd9Sstevel@tonic-gate		# try to detect old non-POSIX types.
617*7c478bd9Sstevel@tonic-gate		# POSIX requires all non-standard typedefs to end in _t,
618*7c478bd9Sstevel@tonic-gate		# but historically these have been used.
619*7c478bd9Sstevel@tonic-gate		if (/\b(unchar|ushort|uint|ulong|u_int|u_short|u_long|u_char|quad)\b/) {
620*7c478bd9Sstevel@tonic-gate			err("non-POSIX typedef $1 used: use $old2posix{$1} instead");
621*7c478bd9Sstevel@tonic-gate		}
622*7c478bd9Sstevel@tonic-gate	}
623*7c478bd9Sstevel@tonic-gate	if ($heuristic) {
624*7c478bd9Sstevel@tonic-gate		# cannot check this everywhere due to "struct {\n...\n} foo;"
625*7c478bd9Sstevel@tonic-gate		if ($in_function && !$in_declaration &&
626*7c478bd9Sstevel@tonic-gate		    /}./ && !/}\s+=/ && !/{.*}[;,]$/ && !/}(\s|)*$/ &&
627*7c478bd9Sstevel@tonic-gate		    !/} (else|while)/ && !/}}/) {
628*7c478bd9Sstevel@tonic-gate			err("possible bad text following right brace");
629*7c478bd9Sstevel@tonic-gate		}
630*7c478bd9Sstevel@tonic-gate		# cannot check this because sub-blocks in
631*7c478bd9Sstevel@tonic-gate		# the middle of code are ok
632*7c478bd9Sstevel@tonic-gate		if ($in_function && /^\s+{/) {
633*7c478bd9Sstevel@tonic-gate			err("possible left brace starting a line");
634*7c478bd9Sstevel@tonic-gate		}
635*7c478bd9Sstevel@tonic-gate	}
636*7c478bd9Sstevel@tonic-gate	if (/^\s*else\W/) {
637*7c478bd9Sstevel@tonic-gate		if ($prev =~ /^\s*}$/) {
638*7c478bd9Sstevel@tonic-gate			err_prefix($prev,
639*7c478bd9Sstevel@tonic-gate			    "else and right brace should be on same line");
640*7c478bd9Sstevel@tonic-gate		}
641*7c478bd9Sstevel@tonic-gate	}
642*7c478bd9Sstevel@tonic-gate	$prev = $line;
643*7c478bd9Sstevel@tonic-gate}
644*7c478bd9Sstevel@tonic-gate
645*7c478bd9Sstevel@tonic-gateif ($prev eq "") {
646*7c478bd9Sstevel@tonic-gate	err("last line in file is blank");
647*7c478bd9Sstevel@tonic-gate}
648*7c478bd9Sstevel@tonic-gate
649*7c478bd9Sstevel@tonic-gate}
650*7c478bd9Sstevel@tonic-gate
651*7c478bd9Sstevel@tonic-gate#
652*7c478bd9Sstevel@tonic-gate# Continuation-line checking
653*7c478bd9Sstevel@tonic-gate#
654*7c478bd9Sstevel@tonic-gate# The rest of this file contains the code for the continuation checking
655*7c478bd9Sstevel@tonic-gate# engine.  It's a pretty simple state machine which tracks the expression
656*7c478bd9Sstevel@tonic-gate# depth (unmatched '('s and '['s).
657*7c478bd9Sstevel@tonic-gate#
658*7c478bd9Sstevel@tonic-gate# Keep in mind that the argument to process_indent() has already been heavily
659*7c478bd9Sstevel@tonic-gate# processed; all comments have been replaced by control-A, and the contents of
660*7c478bd9Sstevel@tonic-gate# strings and character constants have been elided.
661*7c478bd9Sstevel@tonic-gate#
662*7c478bd9Sstevel@tonic-gate
663*7c478bd9Sstevel@tonic-gatemy $cont_in;		# currently inside of a continuation
664*7c478bd9Sstevel@tonic-gatemy $cont_off;		# skipping an initializer or definition
665*7c478bd9Sstevel@tonic-gatemy $cont_noerr;		# suppress cascading errors
666*7c478bd9Sstevel@tonic-gatemy $cont_start;		# the line being continued
667*7c478bd9Sstevel@tonic-gatemy $cont_base;		# the base indentation
668*7c478bd9Sstevel@tonic-gatemy $cont_first;		# this is the first line of a statement
669*7c478bd9Sstevel@tonic-gatemy $cont_multiseg;	# this continuation has multiple segments
670*7c478bd9Sstevel@tonic-gate
671*7c478bd9Sstevel@tonic-gatemy $cont_special;	# this is a C statement (if, for, etc.)
672*7c478bd9Sstevel@tonic-gatemy $cont_macro;		# this is a macro
673*7c478bd9Sstevel@tonic-gatemy $cont_case;		# this is a multi-line case
674*7c478bd9Sstevel@tonic-gate
675*7c478bd9Sstevel@tonic-gatemy @cont_paren;		# the stack of unmatched ( and [s we've seen
676*7c478bd9Sstevel@tonic-gate
677*7c478bd9Sstevel@tonic-gatesub
678*7c478bd9Sstevel@tonic-gatereset_indent()
679*7c478bd9Sstevel@tonic-gate{
680*7c478bd9Sstevel@tonic-gate	$cont_in = 0;
681*7c478bd9Sstevel@tonic-gate	$cont_off = 0;
682*7c478bd9Sstevel@tonic-gate}
683*7c478bd9Sstevel@tonic-gate
684*7c478bd9Sstevel@tonic-gatesub
685*7c478bd9Sstevel@tonic-gatedelabel($)
686*7c478bd9Sstevel@tonic-gate{
687*7c478bd9Sstevel@tonic-gate	#
688*7c478bd9Sstevel@tonic-gate	# replace labels with tabs.  Note that there may be multiple
689*7c478bd9Sstevel@tonic-gate	# labels on a line.
690*7c478bd9Sstevel@tonic-gate	#
691*7c478bd9Sstevel@tonic-gate	local $_ = $_[0];
692*7c478bd9Sstevel@tonic-gate
693*7c478bd9Sstevel@tonic-gate	while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) {
694*7c478bd9Sstevel@tonic-gate		my ($pre_tabs, $label, $rest) = ($1, $2, $3);
695*7c478bd9Sstevel@tonic-gate		$_ = $pre_tabs;
696*7c478bd9Sstevel@tonic-gate		while ($label =~ s/^([^\t]*)(\t+)//) {
697*7c478bd9Sstevel@tonic-gate			$_ .= "\t" x (length($2) + length($1) / 8);
698*7c478bd9Sstevel@tonic-gate		}
699*7c478bd9Sstevel@tonic-gate		$_ .= ("\t" x (length($label) / 8)).$rest;
700*7c478bd9Sstevel@tonic-gate	}
701*7c478bd9Sstevel@tonic-gate
702*7c478bd9Sstevel@tonic-gate	return ($_);
703*7c478bd9Sstevel@tonic-gate}
704*7c478bd9Sstevel@tonic-gate
705*7c478bd9Sstevel@tonic-gatesub
706*7c478bd9Sstevel@tonic-gateprocess_indent($)
707*7c478bd9Sstevel@tonic-gate{
708*7c478bd9Sstevel@tonic-gate	require strict;
709*7c478bd9Sstevel@tonic-gate	local $_ = $_[0];			# preserve the global $_
710*7c478bd9Sstevel@tonic-gate
711*7c478bd9Sstevel@tonic-gate	s///g;	# No comments
712*7c478bd9Sstevel@tonic-gate	s/\s+$//;	# Strip trailing whitespace
713*7c478bd9Sstevel@tonic-gate
714*7c478bd9Sstevel@tonic-gate	return			if (/^$/);	# skip empty lines
715*7c478bd9Sstevel@tonic-gate
716*7c478bd9Sstevel@tonic-gate	# regexps used below; keywords taking (), macros, and continued cases
717*7c478bd9Sstevel@tonic-gate	my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b';
718*7c478bd9Sstevel@tonic-gate	my $macro = '[A-Z_][A-Z_0-9]*\(';
719*7c478bd9Sstevel@tonic-gate	my $case = 'case\b[^:]*$';
720*7c478bd9Sstevel@tonic-gate
721*7c478bd9Sstevel@tonic-gate	# skip over enumerations, array definitions, initializers, etc.
722*7c478bd9Sstevel@tonic-gate	if ($cont_off <= 0 && !/^\s*$special/ &&
723*7c478bd9Sstevel@tonic-gate	    (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*)){/ ||
724*7c478bd9Sstevel@tonic-gate	    (/^\s*{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) {
725*7c478bd9Sstevel@tonic-gate		$cont_in = 0;
726*7c478bd9Sstevel@tonic-gate		$cont_off = tr/{/{/ - tr/}/}/;
727*7c478bd9Sstevel@tonic-gate		return;
728*7c478bd9Sstevel@tonic-gate	}
729*7c478bd9Sstevel@tonic-gate	if ($cont_off) {
730*7c478bd9Sstevel@tonic-gate		$cont_off += tr/{/{/ - tr/}/}/;
731*7c478bd9Sstevel@tonic-gate		return;
732*7c478bd9Sstevel@tonic-gate	}
733*7c478bd9Sstevel@tonic-gate
734*7c478bd9Sstevel@tonic-gate	if (!$cont_in) {
735*7c478bd9Sstevel@tonic-gate		$cont_start = $line;
736*7c478bd9Sstevel@tonic-gate
737*7c478bd9Sstevel@tonic-gate		if (/^\t* /) {
738*7c478bd9Sstevel@tonic-gate			err("non-continuation indented 4 spaces");
739*7c478bd9Sstevel@tonic-gate			$cont_noerr = 1;		# stop reporting
740*7c478bd9Sstevel@tonic-gate		}
741*7c478bd9Sstevel@tonic-gate		$_ = delabel($_);	# replace labels with tabs
742*7c478bd9Sstevel@tonic-gate
743*7c478bd9Sstevel@tonic-gate		# check if the statement is complete
744*7c478bd9Sstevel@tonic-gate		return		if (/^\s*\}?$/);
745*7c478bd9Sstevel@tonic-gate		return		if (/^\s*\}?\s*else\s*\{?$/);
746*7c478bd9Sstevel@tonic-gate		return		if (/^\s*do\s*\{?$/);
747*7c478bd9Sstevel@tonic-gate		return		if (/{$/);
748*7c478bd9Sstevel@tonic-gate		return		if (/}[,;]?$/);
749*7c478bd9Sstevel@tonic-gate
750*7c478bd9Sstevel@tonic-gate		# cases we don't deal with, generally non-kosher
751*7c478bd9Sstevel@tonic-gate		if (/{/) {
752*7c478bd9Sstevel@tonic-gate			err("stuff after {");
753*7c478bd9Sstevel@tonic-gate			return;
754*7c478bd9Sstevel@tonic-gate		}
755*7c478bd9Sstevel@tonic-gate
756*7c478bd9Sstevel@tonic-gate		# Get the base line, and set up the state machine
757*7c478bd9Sstevel@tonic-gate		/^(\t*)/;
758*7c478bd9Sstevel@tonic-gate		$cont_base = $1;
759*7c478bd9Sstevel@tonic-gate		$cont_in = 1;
760*7c478bd9Sstevel@tonic-gate		@cont_paren = ();
761*7c478bd9Sstevel@tonic-gate		$cont_first = 1;
762*7c478bd9Sstevel@tonic-gate		$cont_multiseg = 0;
763*7c478bd9Sstevel@tonic-gate
764*7c478bd9Sstevel@tonic-gate		# certain things need special processing
765*7c478bd9Sstevel@tonic-gate		$cont_special = /^\s*$special/? 1 : 0;
766*7c478bd9Sstevel@tonic-gate		$cont_macro = /^\s*$macro/? 1 : 0;
767*7c478bd9Sstevel@tonic-gate		$cont_case = /^\s*$case/? 1 : 0;
768*7c478bd9Sstevel@tonic-gate	} else {
769*7c478bd9Sstevel@tonic-gate		$cont_first = 0;
770*7c478bd9Sstevel@tonic-gate
771*7c478bd9Sstevel@tonic-gate		# Strings may be pulled back to an earlier (half-)tabstop
772*7c478bd9Sstevel@tonic-gate		unless ($cont_noerr || /^$cont_base    / ||
773*7c478bd9Sstevel@tonic-gate		    (/^\t*(?:    )?(?:gettext\()?\"/ && !/^$cont_base\t/)) {
774*7c478bd9Sstevel@tonic-gate			err_prefix($cont_start,
775*7c478bd9Sstevel@tonic-gate			    "continuation should be indented 4 spaces");
776*7c478bd9Sstevel@tonic-gate		}
777*7c478bd9Sstevel@tonic-gate	}
778*7c478bd9Sstevel@tonic-gate
779*7c478bd9Sstevel@tonic-gate	my $rest = $_;			# keeps the remainder of the line
780*7c478bd9Sstevel@tonic-gate
781*7c478bd9Sstevel@tonic-gate	#
782*7c478bd9Sstevel@tonic-gate	# The split matches 0 characters, so that each 'special' character
783*7c478bd9Sstevel@tonic-gate	# is processed separately.  Parens and brackets are pushed and
784*7c478bd9Sstevel@tonic-gate	# popped off the @cont_paren stack.  For normal processing, we wait
785*7c478bd9Sstevel@tonic-gate	# until a ; or { terminates the statement.  "special" processing
786*7c478bd9Sstevel@tonic-gate	# (if/for/while/switch) is allowed to stop when the stack empties,
787*7c478bd9Sstevel@tonic-gate	# as is macro processing.  Case statements are terminated with a :
788*7c478bd9Sstevel@tonic-gate	# and an empty paren stack.
789*7c478bd9Sstevel@tonic-gate	#
790*7c478bd9Sstevel@tonic-gate	foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) {
791*7c478bd9Sstevel@tonic-gate		next		if (length($_) == 0);
792*7c478bd9Sstevel@tonic-gate
793*7c478bd9Sstevel@tonic-gate		# rest contains the remainder of the line
794*7c478bd9Sstevel@tonic-gate		my $rxp = "[^\Q$_\E]*\Q$_\E";
795*7c478bd9Sstevel@tonic-gate		$rest =~ s/^$rxp//;
796*7c478bd9Sstevel@tonic-gate
797*7c478bd9Sstevel@tonic-gate		if (/\(/ || /\[/) {
798*7c478bd9Sstevel@tonic-gate			push @cont_paren, $_;
799*7c478bd9Sstevel@tonic-gate		} elsif (/\)/ || /\]/) {
800*7c478bd9Sstevel@tonic-gate			my $cur = $_;
801*7c478bd9Sstevel@tonic-gate			tr/\)\]/\(\[/;
802*7c478bd9Sstevel@tonic-gate
803*7c478bd9Sstevel@tonic-gate			my $old = (pop @cont_paren);
804*7c478bd9Sstevel@tonic-gate			if (!defined($old)) {
805*7c478bd9Sstevel@tonic-gate				err("unexpected '$cur'");
806*7c478bd9Sstevel@tonic-gate				$cont_in = 0;
807*7c478bd9Sstevel@tonic-gate				last;
808*7c478bd9Sstevel@tonic-gate			} elsif ($old ne $_) {
809*7c478bd9Sstevel@tonic-gate				err("'$cur' mismatched with '$old'");
810*7c478bd9Sstevel@tonic-gate				$cont_in = 0;
811*7c478bd9Sstevel@tonic-gate				last;
812*7c478bd9Sstevel@tonic-gate			}
813*7c478bd9Sstevel@tonic-gate
814*7c478bd9Sstevel@tonic-gate			#
815*7c478bd9Sstevel@tonic-gate			# If the stack is now empty, do special processing
816*7c478bd9Sstevel@tonic-gate			# for if/for/while/switch and macro statements.
817*7c478bd9Sstevel@tonic-gate			#
818*7c478bd9Sstevel@tonic-gate			next		if (@cont_paren != 0);
819*7c478bd9Sstevel@tonic-gate			if ($cont_special) {
820*7c478bd9Sstevel@tonic-gate				if ($rest =~ /^\s*{?$/) {
821*7c478bd9Sstevel@tonic-gate					$cont_in = 0;
822*7c478bd9Sstevel@tonic-gate					last;
823*7c478bd9Sstevel@tonic-gate				}
824*7c478bd9Sstevel@tonic-gate				if ($rest =~ /^\s*;$/) {
825*7c478bd9Sstevel@tonic-gate					err("empty if/for/while body ".
826*7c478bd9Sstevel@tonic-gate					    "not on its own line");
827*7c478bd9Sstevel@tonic-gate					$cont_in = 0;
828*7c478bd9Sstevel@tonic-gate					last;
829*7c478bd9Sstevel@tonic-gate				}
830*7c478bd9Sstevel@tonic-gate				if (!$cont_first && $cont_multiseg == 1) {
831*7c478bd9Sstevel@tonic-gate					err_prefix($cont_start,
832*7c478bd9Sstevel@tonic-gate					    "multiple statements continued ".
833*7c478bd9Sstevel@tonic-gate					    "over multiple lines");
834*7c478bd9Sstevel@tonic-gate					$cont_multiseg = 2;
835*7c478bd9Sstevel@tonic-gate				} elsif ($cont_multiseg == 0) {
836*7c478bd9Sstevel@tonic-gate					$cont_multiseg = 1;
837*7c478bd9Sstevel@tonic-gate				}
838*7c478bd9Sstevel@tonic-gate				# We've finished this section, start
839*7c478bd9Sstevel@tonic-gate				# processing the next.
840*7c478bd9Sstevel@tonic-gate				goto section_ended;
841*7c478bd9Sstevel@tonic-gate			}
842*7c478bd9Sstevel@tonic-gate			if ($cont_macro) {
843*7c478bd9Sstevel@tonic-gate				if ($rest =~ /^$/) {
844*7c478bd9Sstevel@tonic-gate					$cont_in = 0;
845*7c478bd9Sstevel@tonic-gate					last;
846*7c478bd9Sstevel@tonic-gate				}
847*7c478bd9Sstevel@tonic-gate			}
848*7c478bd9Sstevel@tonic-gate		} elsif (/\;/) {
849*7c478bd9Sstevel@tonic-gate			if ($cont_case) {
850*7c478bd9Sstevel@tonic-gate				err("unexpected ;");
851*7c478bd9Sstevel@tonic-gate			} elsif (!$cont_special) {
852*7c478bd9Sstevel@tonic-gate				err("unexpected ;")	if (@cont_paren != 0);
853*7c478bd9Sstevel@tonic-gate				if (!$cont_first && $cont_multiseg == 1) {
854*7c478bd9Sstevel@tonic-gate					err_prefix($cont_start,
855*7c478bd9Sstevel@tonic-gate					    "multiple statements continued ".
856*7c478bd9Sstevel@tonic-gate					    "over multiple lines");
857*7c478bd9Sstevel@tonic-gate					$cont_multiseg = 2;
858*7c478bd9Sstevel@tonic-gate				} elsif ($cont_multiseg == 0) {
859*7c478bd9Sstevel@tonic-gate					$cont_multiseg = 1;
860*7c478bd9Sstevel@tonic-gate				}
861*7c478bd9Sstevel@tonic-gate				if ($rest =~ /^$/) {
862*7c478bd9Sstevel@tonic-gate					$cont_in = 0;
863*7c478bd9Sstevel@tonic-gate					last;
864*7c478bd9Sstevel@tonic-gate				}
865*7c478bd9Sstevel@tonic-gate				if ($rest =~ /^\s*special/) {
866*7c478bd9Sstevel@tonic-gate					err("if/for/while/switch not started ".
867*7c478bd9Sstevel@tonic-gate					    "on its own line");
868*7c478bd9Sstevel@tonic-gate				}
869*7c478bd9Sstevel@tonic-gate				goto section_ended;
870*7c478bd9Sstevel@tonic-gate			}
871*7c478bd9Sstevel@tonic-gate		} elsif (/\{/) {
872*7c478bd9Sstevel@tonic-gate			err("{ while in parens/brackets") if (@cont_paren != 0);
873*7c478bd9Sstevel@tonic-gate			err("stuff after {")		if ($rest =~ /[^\s}]/);
874*7c478bd9Sstevel@tonic-gate			$cont_in = 0;
875*7c478bd9Sstevel@tonic-gate			last;
876*7c478bd9Sstevel@tonic-gate		} elsif (/\}/) {
877*7c478bd9Sstevel@tonic-gate			err("} while in parens/brackets") if (@cont_paren != 0);
878*7c478bd9Sstevel@tonic-gate			if (!$cont_special && $rest !~ /^\s*(while|else)\b/) {
879*7c478bd9Sstevel@tonic-gate				if ($rest =~ /^$/) {
880*7c478bd9Sstevel@tonic-gate					err("unexpected }");
881*7c478bd9Sstevel@tonic-gate				} else {
882*7c478bd9Sstevel@tonic-gate					err("stuff after }");
883*7c478bd9Sstevel@tonic-gate				}
884*7c478bd9Sstevel@tonic-gate				$cont_in = 0;
885*7c478bd9Sstevel@tonic-gate				last;
886*7c478bd9Sstevel@tonic-gate			}
887*7c478bd9Sstevel@tonic-gate		} elsif (/\:/ && $cont_case && @cont_paren == 0) {
888*7c478bd9Sstevel@tonic-gate			err("stuff after multi-line case") if ($rest !~ /$^/);
889*7c478bd9Sstevel@tonic-gate			$cont_in = 0;
890*7c478bd9Sstevel@tonic-gate			last;
891*7c478bd9Sstevel@tonic-gate		}
892*7c478bd9Sstevel@tonic-gate		next;
893*7c478bd9Sstevel@tonic-gatesection_ended:
894*7c478bd9Sstevel@tonic-gate		# End of a statement or if/while/for loop.  Reset
895*7c478bd9Sstevel@tonic-gate		# cont_special and cont_macro based on the rest of the
896*7c478bd9Sstevel@tonic-gate		# line.
897*7c478bd9Sstevel@tonic-gate		$cont_special = ($rest =~ /^\s*$special/)? 1 : 0;
898*7c478bd9Sstevel@tonic-gate		$cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0;
899*7c478bd9Sstevel@tonic-gate		$cont_case = 0;
900*7c478bd9Sstevel@tonic-gate		next;
901*7c478bd9Sstevel@tonic-gate	}
902*7c478bd9Sstevel@tonic-gate	$cont_noerr = 0			if (!$cont_in);
903*7c478bd9Sstevel@tonic-gate}
904