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