17c478bd9Sstevel@tonic-gate#!/usr/bin/perl -w 27c478bd9Sstevel@tonic-gate# 37c478bd9Sstevel@tonic-gate# CDDL HEADER START 47c478bd9Sstevel@tonic-gate# 57c478bd9Sstevel@tonic-gate# The contents of this file are subject to the terms of the 67c478bd9Sstevel@tonic-gate# Common Development and Distribution License, Version 1.0 only 77c478bd9Sstevel@tonic-gate# (the "License"). You may not use this file except in compliance 87c478bd9Sstevel@tonic-gate# with the License. 97c478bd9Sstevel@tonic-gate# 107c478bd9Sstevel@tonic-gate# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 117c478bd9Sstevel@tonic-gate# or http://www.opensolaris.org/os/licensing. 127c478bd9Sstevel@tonic-gate# See the License for the specific language governing permissions 137c478bd9Sstevel@tonic-gate# and limitations under the License. 147c478bd9Sstevel@tonic-gate# 157c478bd9Sstevel@tonic-gate# When distributing Covered Code, include this CDDL HEADER in each 167c478bd9Sstevel@tonic-gate# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 177c478bd9Sstevel@tonic-gate# If applicable, add the following below this CDDL HEADER, with the 187c478bd9Sstevel@tonic-gate# fields enclosed by brackets "[]" replaced with your own identifying 197c478bd9Sstevel@tonic-gate# information: Portions Copyright [yyyy] [name of copyright owner] 207c478bd9Sstevel@tonic-gate# 217c478bd9Sstevel@tonic-gate# CDDL HEADER END 227c478bd9Sstevel@tonic-gate# 237c478bd9Sstevel@tonic-gate# 247c478bd9Sstevel@tonic-gate# Copyright 2005 Sun Microsystems, Inc. All rights reserved. 257c478bd9Sstevel@tonic-gate# Use is subject to license terms. 267c478bd9Sstevel@tonic-gate# 277c478bd9Sstevel@tonic-gate# @(#)cstyle 1.58 98/09/09 (from shannon) 287c478bd9Sstevel@tonic-gate#ident "%Z%%M% %I% %E% SMI" 297c478bd9Sstevel@tonic-gate# 307c478bd9Sstevel@tonic-gate# cstyle - check for some common stylistic errors. 317c478bd9Sstevel@tonic-gate# 327c478bd9Sstevel@tonic-gate# cstyle is a sort of "lint" for C coding style. 337c478bd9Sstevel@tonic-gate# It attempts to check for the style used in the 347c478bd9Sstevel@tonic-gate# kernel, sometimes known as "Bill Joy Normal Form". 357c478bd9Sstevel@tonic-gate# 367c478bd9Sstevel@tonic-gate# There's a lot this can't check for, like proper indentation 377c478bd9Sstevel@tonic-gate# of code blocks. There's also a lot more this could check for. 387c478bd9Sstevel@tonic-gate# 397c478bd9Sstevel@tonic-gate# A note to the non perl literate: 407c478bd9Sstevel@tonic-gate# 417c478bd9Sstevel@tonic-gate# perl regular expressions are pretty much like egrep 427c478bd9Sstevel@tonic-gate# regular expressions, with the following special symbols 437c478bd9Sstevel@tonic-gate# 447c478bd9Sstevel@tonic-gate# \s any space character 457c478bd9Sstevel@tonic-gate# \S any non-space character 467c478bd9Sstevel@tonic-gate# \w any "word" character [a-zA-Z0-9_] 477c478bd9Sstevel@tonic-gate# \W any non-word character 487c478bd9Sstevel@tonic-gate# \d a digit [0-9] 497c478bd9Sstevel@tonic-gate# \D a non-digit 507c478bd9Sstevel@tonic-gate# \b word boundary (between \w and \W) 517c478bd9Sstevel@tonic-gate# \B non-word boundary 527c478bd9Sstevel@tonic-gate# 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gaterequire 5.0; 557c478bd9Sstevel@tonic-gateuse IO::File; 567c478bd9Sstevel@tonic-gateuse Getopt::Std; 577c478bd9Sstevel@tonic-gateuse strict; 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gatemy $usage = 607c478bd9Sstevel@tonic-gate"usage: cstyle [-c] [-h] [-p] [-v] [-C] [-P] file ... 617c478bd9Sstevel@tonic-gate -c check continuation indentation inside functions 627c478bd9Sstevel@tonic-gate -h perform heuristic checks that are sometimes wrong 637c478bd9Sstevel@tonic-gate -p perform some of the more picky checks 647c478bd9Sstevel@tonic-gate -v verbose 657c478bd9Sstevel@tonic-gate -C don't check anything in header block comments 667c478bd9Sstevel@tonic-gate -P check for use of non-POSIX types 677c478bd9Sstevel@tonic-gate"; 687c478bd9Sstevel@tonic-gate 697c478bd9Sstevel@tonic-gatemy %opts; 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gateif (!getopts("chpvCP", \%opts)) { 727c478bd9Sstevel@tonic-gate print $usage; 737c478bd9Sstevel@tonic-gate exit 1; 747c478bd9Sstevel@tonic-gate} 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gatemy $check_continuation = $opts{'c'}; 777c478bd9Sstevel@tonic-gatemy $heuristic = $opts{'h'}; 787c478bd9Sstevel@tonic-gatemy $picky = $opts{'p'}; 797c478bd9Sstevel@tonic-gatemy $verbose = $opts{'v'}; 807c478bd9Sstevel@tonic-gatemy $ignore_hdr_comment = $opts{'C'}; 817c478bd9Sstevel@tonic-gatemy $check_posix_types = $opts{'P'}; 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gatemy ($filename, $line, $prev); # shared globals 847c478bd9Sstevel@tonic-gate 857c478bd9Sstevel@tonic-gatemy $fmt; 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gateif ($verbose) { 887c478bd9Sstevel@tonic-gate $fmt = "%s: %d: %s\n%s\n"; 897c478bd9Sstevel@tonic-gate} else { 907c478bd9Sstevel@tonic-gate $fmt = "%s: %d: %s\n"; 917c478bd9Sstevel@tonic-gate} 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate# Note, following must be in single quotes so that \s and \w work right. 947c478bd9Sstevel@tonic-gatemy $typename = '(int|char|short|long|unsigned|float|double' . 957c478bd9Sstevel@tonic-gate '|\w+_t|struct\s+\w+|union\s+\w+|FILE)'; 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate# mapping of old types to POSIX compatible types 987c478bd9Sstevel@tonic-gatemy %old2posix = ( 997c478bd9Sstevel@tonic-gate 'unchar' => 'uchar_t', 1007c478bd9Sstevel@tonic-gate 'ushort' => 'ushort_t', 1017c478bd9Sstevel@tonic-gate 'uint' => 'uint_t', 1027c478bd9Sstevel@tonic-gate 'ulong' => 'ulong_t', 1037c478bd9Sstevel@tonic-gate 'u_int' => 'uint_t', 1047c478bd9Sstevel@tonic-gate 'u_short' => 'ushort_t', 1057c478bd9Sstevel@tonic-gate 'u_long' => 'ulong_t', 1067c478bd9Sstevel@tonic-gate 'u_char' => 'uchar_t', 1077c478bd9Sstevel@tonic-gate 'quad' => 'quad_t' 1087c478bd9Sstevel@tonic-gate); 1097c478bd9Sstevel@tonic-gate 1107c478bd9Sstevel@tonic-gatemy $warlock_comment = "( 1117c478bd9Sstevel@tonic-gateVARIABLES PROTECTED BY| 1127c478bd9Sstevel@tonic-gateMEMBERS PROTECTED BY| 1137c478bd9Sstevel@tonic-gateALL MEMBERS PROTECTED BY| 1147c478bd9Sstevel@tonic-gateREAD-ONLY VARIABLES:| 1157c478bd9Sstevel@tonic-gateREAD-ONLY MEMBERS:| 1167c478bd9Sstevel@tonic-gateVARIABLES READABLE WITHOUT LOCK:| 1177c478bd9Sstevel@tonic-gateMEMBERS READABLE WITHOUT LOCK:| 1187c478bd9Sstevel@tonic-gateLOCKS COVERED BY| 1197c478bd9Sstevel@tonic-gateLOCK UNNEEDED BECAUSE| 1207c478bd9Sstevel@tonic-gateLOCK NEEDED:| 1217c478bd9Sstevel@tonic-gateLOCK HELD ON ENTRY:| 1227c478bd9Sstevel@tonic-gateREAD LOCK HELD ON ENTRY:| 1237c478bd9Sstevel@tonic-gateWRITE LOCK HELD ON ENTRY:| 1247c478bd9Sstevel@tonic-gateLOCK ACQUIRED AS SIDE EFFECT:| 1257c478bd9Sstevel@tonic-gateREAD LOCK ACQUIRED AS SIDE EFFECT:| 1267c478bd9Sstevel@tonic-gateWRITE LOCK ACQUIRED AS SIDE EFFECT:| 1277c478bd9Sstevel@tonic-gateLOCK RELEASED AS SIDE EFFECT:| 1287c478bd9Sstevel@tonic-gateLOCK UPGRADED AS SIDE EFFECT:| 1297c478bd9Sstevel@tonic-gateLOCK DOWNGRADED AS SIDE EFFECT:| 1307c478bd9Sstevel@tonic-gateFUNCTIONS CALLED THROUGH POINTER| 1317c478bd9Sstevel@tonic-gateFUNCTIONS CALLED THROUGH MEMBER| 1327c478bd9Sstevel@tonic-gateLOCK ORDER: 1337c478bd9Sstevel@tonic-gate)"; 1347c478bd9Sstevel@tonic-gate$warlock_comment =~ tr/\n//d; 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gateif ($#ARGV >= 0) { 1377c478bd9Sstevel@tonic-gate foreach my $arg (@ARGV) { 1387c478bd9Sstevel@tonic-gate my $fh = new IO::File $arg, "r"; 1397c478bd9Sstevel@tonic-gate if (!defined($fh)) { 1407c478bd9Sstevel@tonic-gate printf "%s: can not open\n", $arg; 1417c478bd9Sstevel@tonic-gate } else { 1427c478bd9Sstevel@tonic-gate &cstyle($arg, $fh); 1437c478bd9Sstevel@tonic-gate close $fh; 1447c478bd9Sstevel@tonic-gate } 1457c478bd9Sstevel@tonic-gate } 1467c478bd9Sstevel@tonic-gate} else { 1477c478bd9Sstevel@tonic-gate &cstyle("<stdin>", *STDIN); 1487c478bd9Sstevel@tonic-gate} 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gatemy $no_errs = 0; # set for CSTYLED-protected lines 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gatesub err($) { 1537c478bd9Sstevel@tonic-gate my ($error) = @_; 1547c478bd9Sstevel@tonic-gate printf $fmt, $filename, $., $error, $line unless ($no_errs); 1557c478bd9Sstevel@tonic-gate} 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gatesub err_prefix($$) { 1587c478bd9Sstevel@tonic-gate my ($prevline, $error) = @_; 1597c478bd9Sstevel@tonic-gate my $out = $prevline."\n".$line; 1607c478bd9Sstevel@tonic-gate printf $fmt, $filename, $., $error, $out unless ($no_errs); 1617c478bd9Sstevel@tonic-gate} 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gatesub err_prev($) { 1647c478bd9Sstevel@tonic-gate my ($error) = @_; 1657c478bd9Sstevel@tonic-gate printf $fmt, $filename, $. - 1, $error, $prev unless ($no_errs); 1667c478bd9Sstevel@tonic-gate} 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gatesub cstyle($$) { 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gatemy ($fn, $filehandle) = @_; 1717c478bd9Sstevel@tonic-gate$filename = $fn; # share it globally 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gatemy $in_cpp = 0; 1747c478bd9Sstevel@tonic-gatemy $next_in_cpp = 0; 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gatemy $in_comment = 0; 1777c478bd9Sstevel@tonic-gatemy $in_header_comment = 0; 1787c478bd9Sstevel@tonic-gatemy $comment_done = 0; 1797c478bd9Sstevel@tonic-gatemy $in_warlock_comment = 0; 1807c478bd9Sstevel@tonic-gatemy $in_function = 0; 1817c478bd9Sstevel@tonic-gatemy $in_function_header = 0; 1827c478bd9Sstevel@tonic-gatemy $in_declaration = 0; 1837c478bd9Sstevel@tonic-gatemy $note_level = 0; 1847c478bd9Sstevel@tonic-gatemy $nextok = 0; 1857c478bd9Sstevel@tonic-gatemy $nocheck = 0; 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gatemy $in_string = 0; 1887c478bd9Sstevel@tonic-gate 1897c478bd9Sstevel@tonic-gatemy ($okmsg, $comment_prefix); 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate$line = ''; 1927c478bd9Sstevel@tonic-gate$prev = ''; 1937c478bd9Sstevel@tonic-gatereset_indent(); 1947c478bd9Sstevel@tonic-gate 1957c478bd9Sstevel@tonic-gateline: while (<$filehandle>) { 1967c478bd9Sstevel@tonic-gate s/\r?\n$//; # strip return and newline 1977c478bd9Sstevel@tonic-gate 1987c478bd9Sstevel@tonic-gate # save the original line, then remove all text from within 1997c478bd9Sstevel@tonic-gate # double or single quotes, we do not want to check such text. 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate $line = $_; 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate # 2047c478bd9Sstevel@tonic-gate # C allows strings to be continued with a backslash at the end of 2057c478bd9Sstevel@tonic-gate # the line. We translate that into a quoted string on the previous 2067c478bd9Sstevel@tonic-gate # line followed by an initial quote on the next line. 2077c478bd9Sstevel@tonic-gate # 2087c478bd9Sstevel@tonic-gate # (we assume that no-one will use backslash-continuation with character 2097c478bd9Sstevel@tonic-gate # constants) 2107c478bd9Sstevel@tonic-gate # 2117c478bd9Sstevel@tonic-gate $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment); 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate # 2147c478bd9Sstevel@tonic-gate # normal strings and characters 2157c478bd9Sstevel@tonic-gate # 2167c478bd9Sstevel@tonic-gate s/'([^\\']|\\[^xX0]|\\0[0-9]*|\\[xX][0-9a-fA-F]*)'/''/g; 2177c478bd9Sstevel@tonic-gate s/"([^\\"]|\\.)*"/\"\"/g; 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate # 2207c478bd9Sstevel@tonic-gate # detect string continuation 2217c478bd9Sstevel@tonic-gate # 2227c478bd9Sstevel@tonic-gate if ($nocheck || $in_comment) { 2237c478bd9Sstevel@tonic-gate $in_string = 0; 2247c478bd9Sstevel@tonic-gate } else { 2257c478bd9Sstevel@tonic-gate # 2267c478bd9Sstevel@tonic-gate # Now that all full strings are replaced with "", we check 2277c478bd9Sstevel@tonic-gate # for unfinished strings continuing onto the next line. 2287c478bd9Sstevel@tonic-gate # 2297c478bd9Sstevel@tonic-gate $in_string = 2307c478bd9Sstevel@tonic-gate (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ || 2317c478bd9Sstevel@tonic-gate s/^("")*"([^\\"]|\\.)*\\$/""/); 2327c478bd9Sstevel@tonic-gate } 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate # 2357c478bd9Sstevel@tonic-gate # figure out if we are in a cpp directive 2367c478bd9Sstevel@tonic-gate # 2377c478bd9Sstevel@tonic-gate $in_cpp = $next_in_cpp || /^\s*#/; # continued or started 2387c478bd9Sstevel@tonic-gate $next_in_cpp = $in_cpp && /\\$/; # only if continued 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate # strip off trailing backslashes, which appear in long macros 2417c478bd9Sstevel@tonic-gate s/\s*\\$//; 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate # an /* END CSTYLED */ comment ends a no-check block. 2447c478bd9Sstevel@tonic-gate if ($nocheck) { 2457c478bd9Sstevel@tonic-gate if (/\/\* *END *CSTYLED *\*\//) { 2467c478bd9Sstevel@tonic-gate $nocheck = 0; 2477c478bd9Sstevel@tonic-gate } else { 2487c478bd9Sstevel@tonic-gate reset_indent(); 2497c478bd9Sstevel@tonic-gate next line; 2507c478bd9Sstevel@tonic-gate } 2517c478bd9Sstevel@tonic-gate } 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate # a /*CSTYLED*/ comment indicates that the next line is ok. 2547c478bd9Sstevel@tonic-gate if ($nextok) { 2557c478bd9Sstevel@tonic-gate if ($okmsg) { 2567c478bd9Sstevel@tonic-gate err($okmsg); 2577c478bd9Sstevel@tonic-gate } 2587c478bd9Sstevel@tonic-gate $nextok = 0; 2597c478bd9Sstevel@tonic-gate $okmsg = 0; 2607c478bd9Sstevel@tonic-gate if (/\/\* *CSTYLED.*\*\//) { 2617c478bd9Sstevel@tonic-gate /^.*\/\* *CSTYLED *(.*) *\*\/.*$/; 2627c478bd9Sstevel@tonic-gate $okmsg = $1; 2637c478bd9Sstevel@tonic-gate $nextok = 1; 2647c478bd9Sstevel@tonic-gate } 2657c478bd9Sstevel@tonic-gate $no_errs = 1; 2667c478bd9Sstevel@tonic-gate } elsif ($no_errs) { 2677c478bd9Sstevel@tonic-gate $no_errs = 0; 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate # check length of line. 2717c478bd9Sstevel@tonic-gate # first, a quick check to see if there is any chance of being too long. 2727c478bd9Sstevel@tonic-gate if (($line =~ tr/\t/\t/) * 7 + length($line) > 80) { 2737c478bd9Sstevel@tonic-gate # yes, there is a chance. 2747c478bd9Sstevel@tonic-gate # replace tabs with spaces and check again. 2757c478bd9Sstevel@tonic-gate my $eline = $line; 2767c478bd9Sstevel@tonic-gate 1 while $eline =~ 2777c478bd9Sstevel@tonic-gate s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e; 2787c478bd9Sstevel@tonic-gate if (length($eline) > 80) { 2797c478bd9Sstevel@tonic-gate err("line > 80 characters"); 2807c478bd9Sstevel@tonic-gate } 2817c478bd9Sstevel@tonic-gate } 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate # ignore NOTE(...) annotations (assumes NOTE is on lines by itself). 2847c478bd9Sstevel@tonic-gate if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE 2857c478bd9Sstevel@tonic-gate s/[^()]//g; # eliminate all non-parens 2867c478bd9Sstevel@tonic-gate $note_level += s/\(//g - length; # update paren nest level 2877c478bd9Sstevel@tonic-gate next; 2887c478bd9Sstevel@tonic-gate } 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate # a /* BEGIN CSTYLED */ comment starts a no-check block. 2917c478bd9Sstevel@tonic-gate if (/\/\* *BEGIN *CSTYLED *\*\//) { 2927c478bd9Sstevel@tonic-gate $nocheck = 1; 2937c478bd9Sstevel@tonic-gate } 2947c478bd9Sstevel@tonic-gate 2957c478bd9Sstevel@tonic-gate # a /*CSTYLED*/ comment indicates that the next line is ok. 2967c478bd9Sstevel@tonic-gate if (/\/\* *CSTYLED.*\*\//) { 2977c478bd9Sstevel@tonic-gate /^.*\/\* *CSTYLED *(.*) *\*\/.*$/; 2987c478bd9Sstevel@tonic-gate $okmsg = $1; 2997c478bd9Sstevel@tonic-gate $nextok = 1; 3007c478bd9Sstevel@tonic-gate } 3017c478bd9Sstevel@tonic-gate if (/\/\/ *CSTYLED/) { 3027c478bd9Sstevel@tonic-gate /^.*\/\/ *CSTYLED *(.*)$/; 3037c478bd9Sstevel@tonic-gate $okmsg = $1; 3047c478bd9Sstevel@tonic-gate $nextok = 1; 3057c478bd9Sstevel@tonic-gate } 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate # universal checks; apply to everything 3087c478bd9Sstevel@tonic-gate if (/\t +\t/) { 3097c478bd9Sstevel@tonic-gate err("spaces between tabs"); 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate if (/ \t+ /) { 3127c478bd9Sstevel@tonic-gate err("tabs between spaces"); 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate if (/\s$/) { 3157c478bd9Sstevel@tonic-gate err("space or tab at end of line"); 3167c478bd9Sstevel@tonic-gate } 3177c478bd9Sstevel@tonic-gate if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) { 3187c478bd9Sstevel@tonic-gate err("comment preceded by non-blank"); 3197c478bd9Sstevel@tonic-gate } 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate # is this the beginning or ending of a function? 3227c478bd9Sstevel@tonic-gate # (not if "struct foo\n{\n") 3237c478bd9Sstevel@tonic-gate if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) { 3247c478bd9Sstevel@tonic-gate $in_function = 1; 3257c478bd9Sstevel@tonic-gate $in_declaration = 1; 3267c478bd9Sstevel@tonic-gate $in_function_header = 0; 3277c478bd9Sstevel@tonic-gate $prev = $line; 3287c478bd9Sstevel@tonic-gate next line; 3297c478bd9Sstevel@tonic-gate } 3307c478bd9Sstevel@tonic-gate if (/^}\s*(\/\*.*\*\/\s*)*$/) { 3317c478bd9Sstevel@tonic-gate if ($prev =~ /^\s*return\s*;/) { 3327c478bd9Sstevel@tonic-gate err_prev("unneeded return at end of function"); 3337c478bd9Sstevel@tonic-gate } 3347c478bd9Sstevel@tonic-gate $in_function = 0; 3357c478bd9Sstevel@tonic-gate reset_indent(); # we don't check between functions 3367c478bd9Sstevel@tonic-gate $prev = $line; 3377c478bd9Sstevel@tonic-gate next line; 3387c478bd9Sstevel@tonic-gate } 3397c478bd9Sstevel@tonic-gate if (/^\w*\($/) { 3407c478bd9Sstevel@tonic-gate $in_function_header = 1; 3417c478bd9Sstevel@tonic-gate } 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate if ($in_warlock_comment && /\*\//) { 3447c478bd9Sstevel@tonic-gate $in_warlock_comment = 0; 3457c478bd9Sstevel@tonic-gate $prev = $line; 3467c478bd9Sstevel@tonic-gate next line; 3477c478bd9Sstevel@tonic-gate } 3487c478bd9Sstevel@tonic-gate 3497c478bd9Sstevel@tonic-gate # a blank line terminates the declarations within a function. 3507c478bd9Sstevel@tonic-gate # XXX - but still a problem in sub-blocks. 3517c478bd9Sstevel@tonic-gate if ($in_declaration && /^$/) { 3527c478bd9Sstevel@tonic-gate $in_declaration = 0; 3537c478bd9Sstevel@tonic-gate } 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate if ($comment_done) { 3567c478bd9Sstevel@tonic-gate $in_comment = 0; 3577c478bd9Sstevel@tonic-gate $in_header_comment = 0; 3587c478bd9Sstevel@tonic-gate $comment_done = 0; 3597c478bd9Sstevel@tonic-gate } 3607c478bd9Sstevel@tonic-gate # does this looks like the start of a block comment? 3617c478bd9Sstevel@tonic-gate if (/^\s*\/\*$/) { 3627c478bd9Sstevel@tonic-gate if (!/^\t*\/\*$/) { 3637c478bd9Sstevel@tonic-gate err("block comment not indented by tabs"); 3647c478bd9Sstevel@tonic-gate } 3657c478bd9Sstevel@tonic-gate $in_comment = 1; 3667c478bd9Sstevel@tonic-gate s/\/\*//; 3677c478bd9Sstevel@tonic-gate $comment_prefix = $_; 3687c478bd9Sstevel@tonic-gate if ($comment_prefix eq "") { 3697c478bd9Sstevel@tonic-gate $in_header_comment = 1; 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate $prev = $line; 3727c478bd9Sstevel@tonic-gate next line; 3737c478bd9Sstevel@tonic-gate } 3747c478bd9Sstevel@tonic-gate # are we still in the block comment? 3757c478bd9Sstevel@tonic-gate if ($in_comment) { 3767c478bd9Sstevel@tonic-gate if (/^$comment_prefix \*\/$/) { 3777c478bd9Sstevel@tonic-gate $comment_done = 1; 3787c478bd9Sstevel@tonic-gate } elsif (/\*\//) { 3797c478bd9Sstevel@tonic-gate $comment_done = 1; 3807c478bd9Sstevel@tonic-gate err("improper block comment close") 3817c478bd9Sstevel@tonic-gate unless ($ignore_hdr_comment && $in_header_comment); 3827c478bd9Sstevel@tonic-gate } elsif (!/^$comment_prefix \*[ \t]/ && 3837c478bd9Sstevel@tonic-gate !/^$comment_prefix \*$/) { 3847c478bd9Sstevel@tonic-gate err("improper block comment") 3857c478bd9Sstevel@tonic-gate unless ($ignore_hdr_comment && $in_header_comment); 3867c478bd9Sstevel@tonic-gate } 3877c478bd9Sstevel@tonic-gate } 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate if ($in_header_comment && $ignore_hdr_comment) { 3907c478bd9Sstevel@tonic-gate $prev = $line; 3917c478bd9Sstevel@tonic-gate next line; 3927c478bd9Sstevel@tonic-gate } 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate # check for errors that might occur in comments and in code. 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate # allow spaces to be used to draw pictures in header comments. 3977c478bd9Sstevel@tonic-gate if (/[^ ] / && !/".* .*"/ && !$in_header_comment) { 3987c478bd9Sstevel@tonic-gate err("spaces instead of tabs"); 3997c478bd9Sstevel@tonic-gate } 4007c478bd9Sstevel@tonic-gate if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ && 4017c478bd9Sstevel@tonic-gate (!/^ \w/ || $in_function != 0)) { 4027c478bd9Sstevel@tonic-gate err("indent by spaces instead of tabs"); 4037c478bd9Sstevel@tonic-gate } 4047c478bd9Sstevel@tonic-gate if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) { 4057c478bd9Sstevel@tonic-gate err("continuation line not indented by 4 spaces"); 4067c478bd9Sstevel@tonic-gate } 4077c478bd9Sstevel@tonic-gate if (/\/\*\s*$warlock_comment/o && !/\*\//) { 4087c478bd9Sstevel@tonic-gate $in_warlock_comment = 1; 4097c478bd9Sstevel@tonic-gate $prev = $line; 4107c478bd9Sstevel@tonic-gate next line; 4117c478bd9Sstevel@tonic-gate } 4127c478bd9Sstevel@tonic-gate if (/^\s*\/\*./ && !/^\s*\/\*.*\*\//) { 4137c478bd9Sstevel@tonic-gate err("improper first line of block comment"); 4147c478bd9Sstevel@tonic-gate } 4157c478bd9Sstevel@tonic-gate 4167c478bd9Sstevel@tonic-gate if ($in_comment) { # still in comment, don't do further checks 4177c478bd9Sstevel@tonic-gate $prev = $line; 4187c478bd9Sstevel@tonic-gate next line; 4197c478bd9Sstevel@tonic-gate } 4207c478bd9Sstevel@tonic-gate 4217c478bd9Sstevel@tonic-gate if ((/[^(]\/\*\S/ || /^\/\*\S/) && 4227c478bd9Sstevel@tonic-gate !(/\/\*(ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*)\*\// || 4237c478bd9Sstevel@tonic-gate /\/\*(CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY)\*\// || 4247c478bd9Sstevel@tonic-gate /\/\*(FALLTHRU|FALLTHROUGH|LINTED.*|PRINTFLIKE[0-9]*)\*\// || 4257c478bd9Sstevel@tonic-gate /\/\*(PROTOLIB[0-9]*|SCANFLIKE[0-9]*|CSTYLED.*)\*\//)) { 4267c478bd9Sstevel@tonic-gate err("missing blank after open comment"); 4277c478bd9Sstevel@tonic-gate } 4287c478bd9Sstevel@tonic-gate if (/\S\*\/[^)]|\S\*\/$/ && 4297c478bd9Sstevel@tonic-gate !(/\/\*(ARGSUSED[0-9]*|NOTREACHED|LINTLIBRARY|VARARGS[0-9]*)\*\// || 4307c478bd9Sstevel@tonic-gate /\/\*(CONSTCOND|CONSTANTCOND|CONSTANTCONDITION|EMPTY)\*\// || 4317c478bd9Sstevel@tonic-gate /\/\*(FALLTHRU|FALLTHROUGH|LINTED.*|PRINTFLIKE[0-9]*)\*\// || 4327c478bd9Sstevel@tonic-gate /\/\*(PROTOLIB[0-9]*|SCANFLIKE[0-9]*|CSTYLED.*)\*\//)) { 4337c478bd9Sstevel@tonic-gate err("missing blank before close comment"); 4347c478bd9Sstevel@tonic-gate } 4357c478bd9Sstevel@tonic-gate if (/\/\/\S/) { # C++ comments 4367c478bd9Sstevel@tonic-gate err("missing blank after start comment"); 4377c478bd9Sstevel@tonic-gate } 4387c478bd9Sstevel@tonic-gate # check for unterminated single line comments, but allow them when 4397c478bd9Sstevel@tonic-gate # they are used to comment out the argument list of a function 4407c478bd9Sstevel@tonic-gate # declaration. 4417c478bd9Sstevel@tonic-gate if (/\S.*\/\*/ && !/\S.*\/\*.*\*\// && !/\(\/\*/) { 4427c478bd9Sstevel@tonic-gate err("unterminated single line comment"); 4437c478bd9Sstevel@tonic-gate } 4447c478bd9Sstevel@tonic-gate 4457c478bd9Sstevel@tonic-gate if (/^(#else|#endif|#include)(.*)$/) { 4467c478bd9Sstevel@tonic-gate $prev = $line; 4477c478bd9Sstevel@tonic-gate if ($picky) { 4487c478bd9Sstevel@tonic-gate my $directive = $1; 4497c478bd9Sstevel@tonic-gate my $clause = $2; 4507c478bd9Sstevel@tonic-gate # Enforce ANSI rules for #else and #endif: no noncomment 4517c478bd9Sstevel@tonic-gate # identifiers are allowed after #endif or #else. Allow 4527c478bd9Sstevel@tonic-gate # C++ comments since they seem to be a fact of life. 4537c478bd9Sstevel@tonic-gate if ((($1 eq "#endif") || ($1 eq "#else")) && 4547c478bd9Sstevel@tonic-gate ($clause ne "") && 4557c478bd9Sstevel@tonic-gate (!($clause =~ /^\s+\/\*.*\*\/$/)) && 4567c478bd9Sstevel@tonic-gate (!($clause =~ /^\s+\/\/.*$/))) { 4577c478bd9Sstevel@tonic-gate err("non-comment text following " . 4587c478bd9Sstevel@tonic-gate "$directive (or malformed $directive " . 4597c478bd9Sstevel@tonic-gate "directive)"); 4607c478bd9Sstevel@tonic-gate } 4617c478bd9Sstevel@tonic-gate } 4627c478bd9Sstevel@tonic-gate next line; 4637c478bd9Sstevel@tonic-gate } 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate # 4667c478bd9Sstevel@tonic-gate # delete any comments and check everything else. Note that 4677c478bd9Sstevel@tonic-gate # ".*?" is a non-greedy match, so that we don't get confused by 4687c478bd9Sstevel@tonic-gate # multiple comments on the same line. 4697c478bd9Sstevel@tonic-gate # 4707c478bd9Sstevel@tonic-gate s/\/\*.*?\*\///g; 4717c478bd9Sstevel@tonic-gate s/\/\/.*$//; # C++ comments 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate # delete any trailing whitespace; we have already checked for that. 4747c478bd9Sstevel@tonic-gate s/\s*$//; 4757c478bd9Sstevel@tonic-gate 4767c478bd9Sstevel@tonic-gate # following checks do not apply to text in comments. 4777c478bd9Sstevel@tonic-gate 4787c478bd9Sstevel@tonic-gate if (/[^<>\s][!<>=]=/ || /[^<>][!<>=]=[^\s,]/ || 4797c478bd9Sstevel@tonic-gate (/[^->]>[^,=>\s]/ && !/[^->]>$/) || 4807c478bd9Sstevel@tonic-gate (/[^<]<[^,=<\s]/ && !/[^<]<$/) || 4817c478bd9Sstevel@tonic-gate /[^<\s]<[^<]/ || /[^->\s]>[^>]/) { 4827c478bd9Sstevel@tonic-gate err("missing space around relational operator"); 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ || 4857c478bd9Sstevel@tonic-gate (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) || 4867c478bd9Sstevel@tonic-gate (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) { 4877c478bd9Sstevel@tonic-gate # XXX - should only check this for C++ code 4887c478bd9Sstevel@tonic-gate # XXX - there are probably other forms that should be allowed 4897c478bd9Sstevel@tonic-gate if (!/\soperator=/) { 4907c478bd9Sstevel@tonic-gate err("missing space around assignment operator"); 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate } 4937c478bd9Sstevel@tonic-gate if (/[,;]\S/ && !/\bfor \(;;\)/) { 4947c478bd9Sstevel@tonic-gate err("comma or semicolon followed by non-blank"); 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate # allow "for" statements to have empty "while" clauses 4977c478bd9Sstevel@tonic-gate if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) { 4987c478bd9Sstevel@tonic-gate err("comma or semicolon preceded by blank"); 4997c478bd9Sstevel@tonic-gate } 5007c478bd9Sstevel@tonic-gate if (/^\s*(&&|\|\|)/) { 5017c478bd9Sstevel@tonic-gate err("improper boolean continuation"); 5027c478bd9Sstevel@tonic-gate } 5037c478bd9Sstevel@tonic-gate if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) { 5047c478bd9Sstevel@tonic-gate err("more than one space around boolean operator"); 5057c478bd9Sstevel@tonic-gate } 5067c478bd9Sstevel@tonic-gate if (/\b(for|if|while|switch|sizeof|return|case)\(/) { 5077c478bd9Sstevel@tonic-gate err("missing space between keyword and paren"); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate if (/(\b(for|if|while|switch|return)\b.*){2,}/ && !/^#define/) { 5107c478bd9Sstevel@tonic-gate # multiple "case" and "sizeof" allowed 5117c478bd9Sstevel@tonic-gate err("more than one keyword on line"); 5127c478bd9Sstevel@tonic-gate } 5137c478bd9Sstevel@tonic-gate if (/\b(for|if|while|switch|sizeof|return|case)\s\s+\(/ && 5147c478bd9Sstevel@tonic-gate !/^#if\s+\(/) { 5157c478bd9Sstevel@tonic-gate err("extra space between keyword and paren"); 5167c478bd9Sstevel@tonic-gate } 5177c478bd9Sstevel@tonic-gate # try to detect "func (x)" but not "if (x)" or 5187c478bd9Sstevel@tonic-gate # "#define foo (x)" or "int (*func)();" 5197c478bd9Sstevel@tonic-gate if (/\w\s\(/) { 5207c478bd9Sstevel@tonic-gate my $s = $_; 5217c478bd9Sstevel@tonic-gate # strip off all keywords on the line 5227c478bd9Sstevel@tonic-gate s/\b(for|if|while|switch|return|case|sizeof)\s\(/XXX(/g; 5237c478bd9Sstevel@tonic-gate s/#elif\s\(/XXX(/g; 5247c478bd9Sstevel@tonic-gate s/^#define\s+\w+\s+\(/XXX(/; 5257c478bd9Sstevel@tonic-gate # do not match things like "void (*f)();" 5267c478bd9Sstevel@tonic-gate # or "typedef void (func_t)();" 5277c478bd9Sstevel@tonic-gate s/\w\s\(+\*/XXX(*/g; 5287c478bd9Sstevel@tonic-gate s/\b($typename|void)\s+\(+/XXX(/og; 5297c478bd9Sstevel@tonic-gate if (/\w\s\(/) { 5307c478bd9Sstevel@tonic-gate err("extra space between function name and left paren"); 5317c478bd9Sstevel@tonic-gate } 5327c478bd9Sstevel@tonic-gate $_ = $s; 5337c478bd9Sstevel@tonic-gate } 5347c478bd9Sstevel@tonic-gate # try to detect "int foo(x)", but not "extern int foo(x);" 5357c478bd9Sstevel@tonic-gate # XXX - this still trips over too many legitimate things, 5367c478bd9Sstevel@tonic-gate # like "int foo(x,\n\ty);" 5377c478bd9Sstevel@tonic-gate# if (/^(\w+(\s|\*)+)+\w+\(/ && !/\)[;,](\s|)*$/ && 5387c478bd9Sstevel@tonic-gate# !/^(extern|static)\b/) { 5397c478bd9Sstevel@tonic-gate# err("return type of function not on separate line"); 5407c478bd9Sstevel@tonic-gate# } 5417c478bd9Sstevel@tonic-gate # this is a close approximation 5427c478bd9Sstevel@tonic-gate if (/^(\w+(\s|\*)+)+\w+\(.*\)(\s|)*$/ && 5437c478bd9Sstevel@tonic-gate !/^(extern|static)\b/) { 5447c478bd9Sstevel@tonic-gate err("return type of function not on separate line"); 5457c478bd9Sstevel@tonic-gate } 5467c478bd9Sstevel@tonic-gate if (/^#define /) { 5477c478bd9Sstevel@tonic-gate err("#define followed by space instead of tab"); 5487c478bd9Sstevel@tonic-gate } 5497c478bd9Sstevel@tonic-gate if (/^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) { 5507c478bd9Sstevel@tonic-gate err("unparenthesized return expression"); 5517c478bd9Sstevel@tonic-gate } 5527c478bd9Sstevel@tonic-gate if (/\bsizeof\b/ && !/\bsizeof\s*\(.*\)/) { 5537c478bd9Sstevel@tonic-gate err("unparenthesized sizeof expression"); 5547c478bd9Sstevel@tonic-gate } 5557c478bd9Sstevel@tonic-gate if (/\(\s/) { 5567c478bd9Sstevel@tonic-gate err("whitespace after left paren"); 5577c478bd9Sstevel@tonic-gate } 5587c478bd9Sstevel@tonic-gate # allow "for" statements to have empty "continue" clauses 5597c478bd9Sstevel@tonic-gate if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) { 5607c478bd9Sstevel@tonic-gate err("whitespace before right paren"); 5617c478bd9Sstevel@tonic-gate } 5627c478bd9Sstevel@tonic-gate if (/^\s*\(void\)[^ ]/) { 5637c478bd9Sstevel@tonic-gate err("missing space after (void) cast"); 5647c478bd9Sstevel@tonic-gate } 5657c478bd9Sstevel@tonic-gate if (/\S{/ && !/{{/) { 5667c478bd9Sstevel@tonic-gate err("missing space before left brace"); 5677c478bd9Sstevel@tonic-gate } 5687c478bd9Sstevel@tonic-gate if ($in_function && /^\s+{/ && 5697c478bd9Sstevel@tonic-gate ($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) { 5707c478bd9Sstevel@tonic-gate err("left brace starting a line"); 5717c478bd9Sstevel@tonic-gate } 5727c478bd9Sstevel@tonic-gate if (/}(else|while)/) { 5737c478bd9Sstevel@tonic-gate err("missing space after right brace"); 5747c478bd9Sstevel@tonic-gate } 5757c478bd9Sstevel@tonic-gate if (/}\s\s+(else|while)/) { 5767c478bd9Sstevel@tonic-gate err("extra space after right brace"); 5777c478bd9Sstevel@tonic-gate } 5787c478bd9Sstevel@tonic-gate if (/\b_VOID\b|\bVOID\b|\bSTATIC\b/) { 5797c478bd9Sstevel@tonic-gate err("obsolete use of VOID or STATIC"); 5807c478bd9Sstevel@tonic-gate } 5817c478bd9Sstevel@tonic-gate if (/\b$typename\*/o) { 5827c478bd9Sstevel@tonic-gate err("missing space between type name and *"); 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate if (/^\s+#/) { 5857c478bd9Sstevel@tonic-gate err("preprocessor statement not in column 1"); 5867c478bd9Sstevel@tonic-gate } 5877c478bd9Sstevel@tonic-gate if (/^#\s/) { 5887c478bd9Sstevel@tonic-gate err("blank after preprocessor #"); 5897c478bd9Sstevel@tonic-gate } 5907c478bd9Sstevel@tonic-gate if (/!\s*(strcmp|strncmp|bcmp)\s*\(/) { 5917c478bd9Sstevel@tonic-gate err("don't use boolean ! with comparison functions"); 5927c478bd9Sstevel@tonic-gate } 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate # 5957c478bd9Sstevel@tonic-gate # We completely ignore, for purposes of indentation: 5967c478bd9Sstevel@tonic-gate # * lines outside of functions 5977c478bd9Sstevel@tonic-gate # * preprocessor lines 5987c478bd9Sstevel@tonic-gate # 5997c478bd9Sstevel@tonic-gate if ($check_continuation && $in_function && !$in_cpp) { 6007c478bd9Sstevel@tonic-gate process_indent($_); 6017c478bd9Sstevel@tonic-gate } 6027c478bd9Sstevel@tonic-gate if ($picky) { 6037c478bd9Sstevel@tonic-gate # try to detect spaces after casts, but allow (e.g.) 6047c478bd9Sstevel@tonic-gate # "sizeof (int) + 1" and "void (*funcptr)(int) = foo;" 6057c478bd9Sstevel@tonic-gate if (/\($typename( \*+)?\)\s/o && 6067c478bd9Sstevel@tonic-gate !/sizeof\s*\($typename( \*)?\)\s/o && 6077c478bd9Sstevel@tonic-gate !/\($typename( \*+)?\)\s+=[^=]/o) { 6087c478bd9Sstevel@tonic-gate err("space after cast"); 6097c478bd9Sstevel@tonic-gate } 6107c478bd9Sstevel@tonic-gate if (/\b$typename\s*\*\s/o && 6117c478bd9Sstevel@tonic-gate !/\b$typename\s*\*\s+const\b/o) { 6127c478bd9Sstevel@tonic-gate err("unary * followed by space"); 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate } 6157c478bd9Sstevel@tonic-gate if ($check_posix_types) { 6167c478bd9Sstevel@tonic-gate # try to detect old non-POSIX types. 6177c478bd9Sstevel@tonic-gate # POSIX requires all non-standard typedefs to end in _t, 6187c478bd9Sstevel@tonic-gate # but historically these have been used. 6197c478bd9Sstevel@tonic-gate if (/\b(unchar|ushort|uint|ulong|u_int|u_short|u_long|u_char|quad)\b/) { 6207c478bd9Sstevel@tonic-gate err("non-POSIX typedef $1 used: use $old2posix{$1} instead"); 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate if ($heuristic) { 6247c478bd9Sstevel@tonic-gate # cannot check this everywhere due to "struct {\n...\n} foo;" 6257c478bd9Sstevel@tonic-gate if ($in_function && !$in_declaration && 6267c478bd9Sstevel@tonic-gate /}./ && !/}\s+=/ && !/{.*}[;,]$/ && !/}(\s|)*$/ && 6277c478bd9Sstevel@tonic-gate !/} (else|while)/ && !/}}/) { 6287c478bd9Sstevel@tonic-gate err("possible bad text following right brace"); 6297c478bd9Sstevel@tonic-gate } 6307c478bd9Sstevel@tonic-gate # cannot check this because sub-blocks in 6317c478bd9Sstevel@tonic-gate # the middle of code are ok 6327c478bd9Sstevel@tonic-gate if ($in_function && /^\s+{/) { 6337c478bd9Sstevel@tonic-gate err("possible left brace starting a line"); 6347c478bd9Sstevel@tonic-gate } 6357c478bd9Sstevel@tonic-gate } 6367c478bd9Sstevel@tonic-gate if (/^\s*else\W/) { 6377c478bd9Sstevel@tonic-gate if ($prev =~ /^\s*}$/) { 6387c478bd9Sstevel@tonic-gate err_prefix($prev, 6397c478bd9Sstevel@tonic-gate "else and right brace should be on same line"); 6407c478bd9Sstevel@tonic-gate } 6417c478bd9Sstevel@tonic-gate } 6427c478bd9Sstevel@tonic-gate $prev = $line; 6437c478bd9Sstevel@tonic-gate} 6447c478bd9Sstevel@tonic-gate 6457c478bd9Sstevel@tonic-gateif ($prev eq "") { 6467c478bd9Sstevel@tonic-gate err("last line in file is blank"); 6477c478bd9Sstevel@tonic-gate} 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate} 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate# 6527c478bd9Sstevel@tonic-gate# Continuation-line checking 6537c478bd9Sstevel@tonic-gate# 6547c478bd9Sstevel@tonic-gate# The rest of this file contains the code for the continuation checking 6557c478bd9Sstevel@tonic-gate# engine. It's a pretty simple state machine which tracks the expression 6567c478bd9Sstevel@tonic-gate# depth (unmatched '('s and '['s). 6577c478bd9Sstevel@tonic-gate# 6587c478bd9Sstevel@tonic-gate# Keep in mind that the argument to process_indent() has already been heavily 6597c478bd9Sstevel@tonic-gate# processed; all comments have been replaced by control-A, and the contents of 6607c478bd9Sstevel@tonic-gate# strings and character constants have been elided. 6617c478bd9Sstevel@tonic-gate# 6627c478bd9Sstevel@tonic-gate 6637c478bd9Sstevel@tonic-gatemy $cont_in; # currently inside of a continuation 6647c478bd9Sstevel@tonic-gatemy $cont_off; # skipping an initializer or definition 6657c478bd9Sstevel@tonic-gatemy $cont_noerr; # suppress cascading errors 6667c478bd9Sstevel@tonic-gatemy $cont_start; # the line being continued 6677c478bd9Sstevel@tonic-gatemy $cont_base; # the base indentation 6687c478bd9Sstevel@tonic-gatemy $cont_first; # this is the first line of a statement 6697c478bd9Sstevel@tonic-gatemy $cont_multiseg; # this continuation has multiple segments 6707c478bd9Sstevel@tonic-gate 6717c478bd9Sstevel@tonic-gatemy $cont_special; # this is a C statement (if, for, etc.) 6727c478bd9Sstevel@tonic-gatemy $cont_macro; # this is a macro 6737c478bd9Sstevel@tonic-gatemy $cont_case; # this is a multi-line case 6747c478bd9Sstevel@tonic-gate 6757c478bd9Sstevel@tonic-gatemy @cont_paren; # the stack of unmatched ( and [s we've seen 6767c478bd9Sstevel@tonic-gate 6777c478bd9Sstevel@tonic-gatesub 6787c478bd9Sstevel@tonic-gatereset_indent() 6797c478bd9Sstevel@tonic-gate{ 6807c478bd9Sstevel@tonic-gate $cont_in = 0; 6817c478bd9Sstevel@tonic-gate $cont_off = 0; 6827c478bd9Sstevel@tonic-gate} 6837c478bd9Sstevel@tonic-gate 6847c478bd9Sstevel@tonic-gatesub 6857c478bd9Sstevel@tonic-gatedelabel($) 6867c478bd9Sstevel@tonic-gate{ 6877c478bd9Sstevel@tonic-gate # 6887c478bd9Sstevel@tonic-gate # replace labels with tabs. Note that there may be multiple 6897c478bd9Sstevel@tonic-gate # labels on a line. 6907c478bd9Sstevel@tonic-gate # 6917c478bd9Sstevel@tonic-gate local $_ = $_[0]; 6927c478bd9Sstevel@tonic-gate 6937c478bd9Sstevel@tonic-gate while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) { 6947c478bd9Sstevel@tonic-gate my ($pre_tabs, $label, $rest) = ($1, $2, $3); 6957c478bd9Sstevel@tonic-gate $_ = $pre_tabs; 6967c478bd9Sstevel@tonic-gate while ($label =~ s/^([^\t]*)(\t+)//) { 6977c478bd9Sstevel@tonic-gate $_ .= "\t" x (length($2) + length($1) / 8); 6987c478bd9Sstevel@tonic-gate } 6997c478bd9Sstevel@tonic-gate $_ .= ("\t" x (length($label) / 8)).$rest; 7007c478bd9Sstevel@tonic-gate } 7017c478bd9Sstevel@tonic-gate 7027c478bd9Sstevel@tonic-gate return ($_); 7037c478bd9Sstevel@tonic-gate} 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gatesub 7067c478bd9Sstevel@tonic-gateprocess_indent($) 7077c478bd9Sstevel@tonic-gate{ 7087c478bd9Sstevel@tonic-gate require strict; 7097c478bd9Sstevel@tonic-gate local $_ = $_[0]; # preserve the global $_ 7107c478bd9Sstevel@tonic-gate 7117c478bd9Sstevel@tonic-gate s///g; # No comments 7127c478bd9Sstevel@tonic-gate s/\s+$//; # Strip trailing whitespace 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate return if (/^$/); # skip empty lines 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate # regexps used below; keywords taking (), macros, and continued cases 7177c478bd9Sstevel@tonic-gate my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b'; 7187c478bd9Sstevel@tonic-gate my $macro = '[A-Z_][A-Z_0-9]*\('; 7197c478bd9Sstevel@tonic-gate my $case = 'case\b[^:]*$'; 7207c478bd9Sstevel@tonic-gate 7217c478bd9Sstevel@tonic-gate # skip over enumerations, array definitions, initializers, etc. 7227c478bd9Sstevel@tonic-gate if ($cont_off <= 0 && !/^\s*$special/ && 7237c478bd9Sstevel@tonic-gate (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*)){/ || 7247c478bd9Sstevel@tonic-gate (/^\s*{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) { 7257c478bd9Sstevel@tonic-gate $cont_in = 0; 7267c478bd9Sstevel@tonic-gate $cont_off = tr/{/{/ - tr/}/}/; 7277c478bd9Sstevel@tonic-gate return; 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate if ($cont_off) { 7307c478bd9Sstevel@tonic-gate $cont_off += tr/{/{/ - tr/}/}/; 7317c478bd9Sstevel@tonic-gate return; 7327c478bd9Sstevel@tonic-gate } 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate if (!$cont_in) { 7357c478bd9Sstevel@tonic-gate $cont_start = $line; 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate if (/^\t* /) { 7387c478bd9Sstevel@tonic-gate err("non-continuation indented 4 spaces"); 7397c478bd9Sstevel@tonic-gate $cont_noerr = 1; # stop reporting 7407c478bd9Sstevel@tonic-gate } 7417c478bd9Sstevel@tonic-gate $_ = delabel($_); # replace labels with tabs 7427c478bd9Sstevel@tonic-gate 7437c478bd9Sstevel@tonic-gate # check if the statement is complete 7447c478bd9Sstevel@tonic-gate return if (/^\s*\}?$/); 7457c478bd9Sstevel@tonic-gate return if (/^\s*\}?\s*else\s*\{?$/); 7467c478bd9Sstevel@tonic-gate return if (/^\s*do\s*\{?$/); 7477c478bd9Sstevel@tonic-gate return if (/{$/); 7487c478bd9Sstevel@tonic-gate return if (/}[,;]?$/); 7497c478bd9Sstevel@tonic-gate 750*6f55da5aSjwadams # Allow macros on their own lines 751*6f55da5aSjwadams return if (/^\s*[A-Z_][A-Z_0-9]*$/); 752*6f55da5aSjwadams 7537c478bd9Sstevel@tonic-gate # cases we don't deal with, generally non-kosher 7547c478bd9Sstevel@tonic-gate if (/{/) { 7557c478bd9Sstevel@tonic-gate err("stuff after {"); 7567c478bd9Sstevel@tonic-gate return; 7577c478bd9Sstevel@tonic-gate } 7587c478bd9Sstevel@tonic-gate 7597c478bd9Sstevel@tonic-gate # Get the base line, and set up the state machine 7607c478bd9Sstevel@tonic-gate /^(\t*)/; 7617c478bd9Sstevel@tonic-gate $cont_base = $1; 7627c478bd9Sstevel@tonic-gate $cont_in = 1; 7637c478bd9Sstevel@tonic-gate @cont_paren = (); 7647c478bd9Sstevel@tonic-gate $cont_first = 1; 7657c478bd9Sstevel@tonic-gate $cont_multiseg = 0; 7667c478bd9Sstevel@tonic-gate 7677c478bd9Sstevel@tonic-gate # certain things need special processing 7687c478bd9Sstevel@tonic-gate $cont_special = /^\s*$special/? 1 : 0; 7697c478bd9Sstevel@tonic-gate $cont_macro = /^\s*$macro/? 1 : 0; 7707c478bd9Sstevel@tonic-gate $cont_case = /^\s*$case/? 1 : 0; 7717c478bd9Sstevel@tonic-gate } else { 7727c478bd9Sstevel@tonic-gate $cont_first = 0; 7737c478bd9Sstevel@tonic-gate 7747c478bd9Sstevel@tonic-gate # Strings may be pulled back to an earlier (half-)tabstop 7757c478bd9Sstevel@tonic-gate unless ($cont_noerr || /^$cont_base / || 7767c478bd9Sstevel@tonic-gate (/^\t*(?: )?(?:gettext\()?\"/ && !/^$cont_base\t/)) { 7777c478bd9Sstevel@tonic-gate err_prefix($cont_start, 7787c478bd9Sstevel@tonic-gate "continuation should be indented 4 spaces"); 7797c478bd9Sstevel@tonic-gate } 7807c478bd9Sstevel@tonic-gate } 7817c478bd9Sstevel@tonic-gate 7827c478bd9Sstevel@tonic-gate my $rest = $_; # keeps the remainder of the line 7837c478bd9Sstevel@tonic-gate 7847c478bd9Sstevel@tonic-gate # 7857c478bd9Sstevel@tonic-gate # The split matches 0 characters, so that each 'special' character 7867c478bd9Sstevel@tonic-gate # is processed separately. Parens and brackets are pushed and 7877c478bd9Sstevel@tonic-gate # popped off the @cont_paren stack. For normal processing, we wait 7887c478bd9Sstevel@tonic-gate # until a ; or { terminates the statement. "special" processing 7897c478bd9Sstevel@tonic-gate # (if/for/while/switch) is allowed to stop when the stack empties, 7907c478bd9Sstevel@tonic-gate # as is macro processing. Case statements are terminated with a : 7917c478bd9Sstevel@tonic-gate # and an empty paren stack. 7927c478bd9Sstevel@tonic-gate # 7937c478bd9Sstevel@tonic-gate foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) { 7947c478bd9Sstevel@tonic-gate next if (length($_) == 0); 7957c478bd9Sstevel@tonic-gate 7967c478bd9Sstevel@tonic-gate # rest contains the remainder of the line 7977c478bd9Sstevel@tonic-gate my $rxp = "[^\Q$_\E]*\Q$_\E"; 7987c478bd9Sstevel@tonic-gate $rest =~ s/^$rxp//; 7997c478bd9Sstevel@tonic-gate 8007c478bd9Sstevel@tonic-gate if (/\(/ || /\[/) { 8017c478bd9Sstevel@tonic-gate push @cont_paren, $_; 8027c478bd9Sstevel@tonic-gate } elsif (/\)/ || /\]/) { 8037c478bd9Sstevel@tonic-gate my $cur = $_; 8047c478bd9Sstevel@tonic-gate tr/\)\]/\(\[/; 8057c478bd9Sstevel@tonic-gate 8067c478bd9Sstevel@tonic-gate my $old = (pop @cont_paren); 8077c478bd9Sstevel@tonic-gate if (!defined($old)) { 8087c478bd9Sstevel@tonic-gate err("unexpected '$cur'"); 8097c478bd9Sstevel@tonic-gate $cont_in = 0; 8107c478bd9Sstevel@tonic-gate last; 8117c478bd9Sstevel@tonic-gate } elsif ($old ne $_) { 8127c478bd9Sstevel@tonic-gate err("'$cur' mismatched with '$old'"); 8137c478bd9Sstevel@tonic-gate $cont_in = 0; 8147c478bd9Sstevel@tonic-gate last; 8157c478bd9Sstevel@tonic-gate } 8167c478bd9Sstevel@tonic-gate 8177c478bd9Sstevel@tonic-gate # 8187c478bd9Sstevel@tonic-gate # If the stack is now empty, do special processing 8197c478bd9Sstevel@tonic-gate # for if/for/while/switch and macro statements. 8207c478bd9Sstevel@tonic-gate # 8217c478bd9Sstevel@tonic-gate next if (@cont_paren != 0); 8227c478bd9Sstevel@tonic-gate if ($cont_special) { 8237c478bd9Sstevel@tonic-gate if ($rest =~ /^\s*{?$/) { 8247c478bd9Sstevel@tonic-gate $cont_in = 0; 8257c478bd9Sstevel@tonic-gate last; 8267c478bd9Sstevel@tonic-gate } 8277c478bd9Sstevel@tonic-gate if ($rest =~ /^\s*;$/) { 8287c478bd9Sstevel@tonic-gate err("empty if/for/while body ". 8297c478bd9Sstevel@tonic-gate "not on its own line"); 8307c478bd9Sstevel@tonic-gate $cont_in = 0; 8317c478bd9Sstevel@tonic-gate last; 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate if (!$cont_first && $cont_multiseg == 1) { 8347c478bd9Sstevel@tonic-gate err_prefix($cont_start, 8357c478bd9Sstevel@tonic-gate "multiple statements continued ". 8367c478bd9Sstevel@tonic-gate "over multiple lines"); 8377c478bd9Sstevel@tonic-gate $cont_multiseg = 2; 8387c478bd9Sstevel@tonic-gate } elsif ($cont_multiseg == 0) { 8397c478bd9Sstevel@tonic-gate $cont_multiseg = 1; 8407c478bd9Sstevel@tonic-gate } 8417c478bd9Sstevel@tonic-gate # We've finished this section, start 8427c478bd9Sstevel@tonic-gate # processing the next. 8437c478bd9Sstevel@tonic-gate goto section_ended; 8447c478bd9Sstevel@tonic-gate } 8457c478bd9Sstevel@tonic-gate if ($cont_macro) { 8467c478bd9Sstevel@tonic-gate if ($rest =~ /^$/) { 8477c478bd9Sstevel@tonic-gate $cont_in = 0; 8487c478bd9Sstevel@tonic-gate last; 8497c478bd9Sstevel@tonic-gate } 8507c478bd9Sstevel@tonic-gate } 8517c478bd9Sstevel@tonic-gate } elsif (/\;/) { 8527c478bd9Sstevel@tonic-gate if ($cont_case) { 8537c478bd9Sstevel@tonic-gate err("unexpected ;"); 8547c478bd9Sstevel@tonic-gate } elsif (!$cont_special) { 8557c478bd9Sstevel@tonic-gate err("unexpected ;") if (@cont_paren != 0); 8567c478bd9Sstevel@tonic-gate if (!$cont_first && $cont_multiseg == 1) { 8577c478bd9Sstevel@tonic-gate err_prefix($cont_start, 8587c478bd9Sstevel@tonic-gate "multiple statements continued ". 8597c478bd9Sstevel@tonic-gate "over multiple lines"); 8607c478bd9Sstevel@tonic-gate $cont_multiseg = 2; 8617c478bd9Sstevel@tonic-gate } elsif ($cont_multiseg == 0) { 8627c478bd9Sstevel@tonic-gate $cont_multiseg = 1; 8637c478bd9Sstevel@tonic-gate } 8647c478bd9Sstevel@tonic-gate if ($rest =~ /^$/) { 8657c478bd9Sstevel@tonic-gate $cont_in = 0; 8667c478bd9Sstevel@tonic-gate last; 8677c478bd9Sstevel@tonic-gate } 8687c478bd9Sstevel@tonic-gate if ($rest =~ /^\s*special/) { 8697c478bd9Sstevel@tonic-gate err("if/for/while/switch not started ". 8707c478bd9Sstevel@tonic-gate "on its own line"); 8717c478bd9Sstevel@tonic-gate } 8727c478bd9Sstevel@tonic-gate goto section_ended; 8737c478bd9Sstevel@tonic-gate } 8747c478bd9Sstevel@tonic-gate } elsif (/\{/) { 8757c478bd9Sstevel@tonic-gate err("{ while in parens/brackets") if (@cont_paren != 0); 8767c478bd9Sstevel@tonic-gate err("stuff after {") if ($rest =~ /[^\s}]/); 8777c478bd9Sstevel@tonic-gate $cont_in = 0; 8787c478bd9Sstevel@tonic-gate last; 8797c478bd9Sstevel@tonic-gate } elsif (/\}/) { 8807c478bd9Sstevel@tonic-gate err("} while in parens/brackets") if (@cont_paren != 0); 8817c478bd9Sstevel@tonic-gate if (!$cont_special && $rest !~ /^\s*(while|else)\b/) { 8827c478bd9Sstevel@tonic-gate if ($rest =~ /^$/) { 8837c478bd9Sstevel@tonic-gate err("unexpected }"); 8847c478bd9Sstevel@tonic-gate } else { 8857c478bd9Sstevel@tonic-gate err("stuff after }"); 8867c478bd9Sstevel@tonic-gate } 8877c478bd9Sstevel@tonic-gate $cont_in = 0; 8887c478bd9Sstevel@tonic-gate last; 8897c478bd9Sstevel@tonic-gate } 8907c478bd9Sstevel@tonic-gate } elsif (/\:/ && $cont_case && @cont_paren == 0) { 8917c478bd9Sstevel@tonic-gate err("stuff after multi-line case") if ($rest !~ /$^/); 8927c478bd9Sstevel@tonic-gate $cont_in = 0; 8937c478bd9Sstevel@tonic-gate last; 8947c478bd9Sstevel@tonic-gate } 8957c478bd9Sstevel@tonic-gate next; 8967c478bd9Sstevel@tonic-gatesection_ended: 8977c478bd9Sstevel@tonic-gate # End of a statement or if/while/for loop. Reset 8987c478bd9Sstevel@tonic-gate # cont_special and cont_macro based on the rest of the 8997c478bd9Sstevel@tonic-gate # line. 9007c478bd9Sstevel@tonic-gate $cont_special = ($rest =~ /^\s*$special/)? 1 : 0; 9017c478bd9Sstevel@tonic-gate $cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0; 9027c478bd9Sstevel@tonic-gate $cont_case = 0; 9037c478bd9Sstevel@tonic-gate next; 9047c478bd9Sstevel@tonic-gate } 9057c478bd9Sstevel@tonic-gate $cont_noerr = 0 if (!$cont_in); 9067c478bd9Sstevel@tonic-gate} 907