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 6cdf0c1d5Smjnelson# Common Development and Distribution License (the "License"). 7cdf0c1d5Smjnelson# 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# 22*94385aa4SToomas Soome# Copyright 2015 Toomas Soome <tsoome@me.com> 237c478bd9Sstevel@tonic-gate# 24cdf0c1d5Smjnelson# Copyright 2008 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# 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; 76cdf0c1d5Smjnelson 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; 98cdf0c1d5Smjnelson 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 172cdf0c1d5Smjnelsonmy $err_stat = 0; # exit status 173cdf0c1d5Smjnelson 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} 187cdf0c1d5Smjnelsonexit $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) = @_; 193cdf0c1d5Smjnelson unless ($no_errs) { 194*94385aa4SToomas Soome if ($verbose) { 195cdf0c1d5Smjnelson printf $fmt, $filename, $., $error, $line; 196*94385aa4SToomas Soome } else { 197*94385aa4SToomas Soome printf $fmt, $filename, $., $error; 198*94385aa4SToomas Soome } 199cdf0c1d5Smjnelson $err_stat = 1; 200cdf0c1d5Smjnelson } 2017c478bd9Sstevel@tonic-gate} 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gatesub err_prefix($$) { 2047c478bd9Sstevel@tonic-gate my ($prevline, $error) = @_; 2057c478bd9Sstevel@tonic-gate my $out = $prevline."\n".$line; 206cdf0c1d5Smjnelson unless ($no_errs) { 207*94385aa4SToomas Soome if ($verbose) { 208cdf0c1d5Smjnelson printf $fmt, $filename, $., $error, $out; 209*94385aa4SToomas Soome } else { 210*94385aa4SToomas Soome printf $fmt, $filename, $., $error; 211*94385aa4SToomas Soome } 212cdf0c1d5Smjnelson $err_stat = 1; 213cdf0c1d5Smjnelson } 2147c478bd9Sstevel@tonic-gate} 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gatesub err_prev($) { 2177c478bd9Sstevel@tonic-gate my ($error) = @_; 218cdf0c1d5Smjnelson unless ($no_errs) { 219*94385aa4SToomas Soome if ($verbose) { 220cdf0c1d5Smjnelson printf $fmt, $filename, $. - 1, $error, $prev; 221*94385aa4SToomas Soome } else { 222*94385aa4SToomas Soome printf $fmt, $filename, $. - 1, $error; 223*94385aa4SToomas Soome } 224cdf0c1d5Smjnelson $err_stat = 1; 225cdf0c1d5Smjnelson } 2267c478bd9Sstevel@tonic-gate} 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gatesub cstyle($$) { 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gatemy ($fn, $filehandle) = @_; 2317c478bd9Sstevel@tonic-gate$filename = $fn; # share it globally 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gatemy $in_cpp = 0; 2347c478bd9Sstevel@tonic-gatemy $next_in_cpp = 0; 2357c478bd9Sstevel@tonic-gate 2367c478bd9Sstevel@tonic-gatemy $in_comment = 0; 2377c478bd9Sstevel@tonic-gatemy $in_header_comment = 0; 2387c478bd9Sstevel@tonic-gatemy $comment_done = 0; 2397c478bd9Sstevel@tonic-gatemy $in_warlock_comment = 0; 2407c478bd9Sstevel@tonic-gatemy $in_function = 0; 2417c478bd9Sstevel@tonic-gatemy $in_function_header = 0; 2427c478bd9Sstevel@tonic-gatemy $in_declaration = 0; 2437c478bd9Sstevel@tonic-gatemy $note_level = 0; 2447c478bd9Sstevel@tonic-gatemy $nextok = 0; 2457c478bd9Sstevel@tonic-gatemy $nocheck = 0; 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gatemy $in_string = 0; 2487c478bd9Sstevel@tonic-gate 2497c478bd9Sstevel@tonic-gatemy ($okmsg, $comment_prefix); 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate$line = ''; 2527c478bd9Sstevel@tonic-gate$prev = ''; 2537c478bd9Sstevel@tonic-gatereset_indent(); 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gateline: while (<$filehandle>) { 2567c478bd9Sstevel@tonic-gate s/\r?\n$//; # strip return and newline 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate # save the original line, then remove all text from within 2597c478bd9Sstevel@tonic-gate # double or single quotes, we do not want to check such text. 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate $line = $_; 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate # 2647c478bd9Sstevel@tonic-gate # C allows strings to be continued with a backslash at the end of 2657c478bd9Sstevel@tonic-gate # the line. We translate that into a quoted string on the previous 2667c478bd9Sstevel@tonic-gate # line followed by an initial quote on the next line. 2677c478bd9Sstevel@tonic-gate # 2687c478bd9Sstevel@tonic-gate # (we assume that no-one will use backslash-continuation with character 2697c478bd9Sstevel@tonic-gate # constants) 2707c478bd9Sstevel@tonic-gate # 2717c478bd9Sstevel@tonic-gate $_ = '"' . $_ if ($in_string && !$nocheck && !$in_comment); 2727c478bd9Sstevel@tonic-gate 2737c478bd9Sstevel@tonic-gate # 2747c478bd9Sstevel@tonic-gate # normal strings and characters 2757c478bd9Sstevel@tonic-gate # 2767c478bd9Sstevel@tonic-gate s/'([^\\']|\\[^xX0]|\\0[0-9]*|\\[xX][0-9a-fA-F]*)'/''/g; 2777c478bd9Sstevel@tonic-gate s/"([^\\"]|\\.)*"/\"\"/g; 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate # 2807c478bd9Sstevel@tonic-gate # detect string continuation 2817c478bd9Sstevel@tonic-gate # 2827c478bd9Sstevel@tonic-gate if ($nocheck || $in_comment) { 2837c478bd9Sstevel@tonic-gate $in_string = 0; 2847c478bd9Sstevel@tonic-gate } else { 2857c478bd9Sstevel@tonic-gate # 2867c478bd9Sstevel@tonic-gate # Now that all full strings are replaced with "", we check 2877c478bd9Sstevel@tonic-gate # for unfinished strings continuing onto the next line. 2887c478bd9Sstevel@tonic-gate # 2897c478bd9Sstevel@tonic-gate $in_string = 2907c478bd9Sstevel@tonic-gate (s/([^"](?:"")*)"([^\\"]|\\.)*\\$/$1""/ || 2917c478bd9Sstevel@tonic-gate s/^("")*"([^\\"]|\\.)*\\$/""/); 2927c478bd9Sstevel@tonic-gate } 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate # 2957c478bd9Sstevel@tonic-gate # figure out if we are in a cpp directive 2967c478bd9Sstevel@tonic-gate # 2977c478bd9Sstevel@tonic-gate $in_cpp = $next_in_cpp || /^\s*#/; # continued or started 2987c478bd9Sstevel@tonic-gate $next_in_cpp = $in_cpp && /\\$/; # only if continued 2997c478bd9Sstevel@tonic-gate 3007c478bd9Sstevel@tonic-gate # strip off trailing backslashes, which appear in long macros 3017c478bd9Sstevel@tonic-gate s/\s*\\$//; 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate # an /* END CSTYLED */ comment ends a no-check block. 3047c478bd9Sstevel@tonic-gate if ($nocheck) { 3057c478bd9Sstevel@tonic-gate if (/\/\* *END *CSTYLED *\*\//) { 3067c478bd9Sstevel@tonic-gate $nocheck = 0; 3077c478bd9Sstevel@tonic-gate } else { 3087c478bd9Sstevel@tonic-gate reset_indent(); 3097c478bd9Sstevel@tonic-gate next line; 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate } 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate # a /*CSTYLED*/ comment indicates that the next line is ok. 3147c478bd9Sstevel@tonic-gate if ($nextok) { 3157c478bd9Sstevel@tonic-gate if ($okmsg) { 3167c478bd9Sstevel@tonic-gate err($okmsg); 3177c478bd9Sstevel@tonic-gate } 3187c478bd9Sstevel@tonic-gate $nextok = 0; 3197c478bd9Sstevel@tonic-gate $okmsg = 0; 3207c478bd9Sstevel@tonic-gate if (/\/\* *CSTYLED.*\*\//) { 3217c478bd9Sstevel@tonic-gate /^.*\/\* *CSTYLED *(.*) *\*\/.*$/; 3227c478bd9Sstevel@tonic-gate $okmsg = $1; 3237c478bd9Sstevel@tonic-gate $nextok = 1; 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate $no_errs = 1; 3267c478bd9Sstevel@tonic-gate } elsif ($no_errs) { 3277c478bd9Sstevel@tonic-gate $no_errs = 0; 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate # check length of line. 3317c478bd9Sstevel@tonic-gate # first, a quick check to see if there is any chance of being too long. 3327c478bd9Sstevel@tonic-gate if (($line =~ tr/\t/\t/) * 7 + length($line) > 80) { 3337c478bd9Sstevel@tonic-gate # yes, there is a chance. 3347c478bd9Sstevel@tonic-gate # replace tabs with spaces and check again. 3357c478bd9Sstevel@tonic-gate my $eline = $line; 3367c478bd9Sstevel@tonic-gate 1 while $eline =~ 3377c478bd9Sstevel@tonic-gate s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e; 3387c478bd9Sstevel@tonic-gate if (length($eline) > 80) { 3397c478bd9Sstevel@tonic-gate err("line > 80 characters"); 3407c478bd9Sstevel@tonic-gate } 3417c478bd9Sstevel@tonic-gate } 3427c478bd9Sstevel@tonic-gate 3437c478bd9Sstevel@tonic-gate # ignore NOTE(...) annotations (assumes NOTE is on lines by itself). 3447c478bd9Sstevel@tonic-gate if ($note_level || /\b_?NOTE\s*\(/) { # if in NOTE or this is NOTE 3457c478bd9Sstevel@tonic-gate s/[^()]//g; # eliminate all non-parens 3467c478bd9Sstevel@tonic-gate $note_level += s/\(//g - length; # update paren nest level 3477c478bd9Sstevel@tonic-gate next; 3487c478bd9Sstevel@tonic-gate } 3497c478bd9Sstevel@tonic-gate 3507c478bd9Sstevel@tonic-gate # a /* BEGIN CSTYLED */ comment starts a no-check block. 3517c478bd9Sstevel@tonic-gate if (/\/\* *BEGIN *CSTYLED *\*\//) { 3527c478bd9Sstevel@tonic-gate $nocheck = 1; 3537c478bd9Sstevel@tonic-gate } 3547c478bd9Sstevel@tonic-gate 3557c478bd9Sstevel@tonic-gate # a /*CSTYLED*/ comment indicates that the next line is ok. 3567c478bd9Sstevel@tonic-gate if (/\/\* *CSTYLED.*\*\//) { 3577c478bd9Sstevel@tonic-gate /^.*\/\* *CSTYLED *(.*) *\*\/.*$/; 3587c478bd9Sstevel@tonic-gate $okmsg = $1; 3597c478bd9Sstevel@tonic-gate $nextok = 1; 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate if (/\/\/ *CSTYLED/) { 3627c478bd9Sstevel@tonic-gate /^.*\/\/ *CSTYLED *(.*)$/; 3637c478bd9Sstevel@tonic-gate $okmsg = $1; 3647c478bd9Sstevel@tonic-gate $nextok = 1; 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate # universal checks; apply to everything 3687c478bd9Sstevel@tonic-gate if (/\t +\t/) { 3697c478bd9Sstevel@tonic-gate err("spaces between tabs"); 3707c478bd9Sstevel@tonic-gate } 3717c478bd9Sstevel@tonic-gate if (/ \t+ /) { 3727c478bd9Sstevel@tonic-gate err("tabs between spaces"); 3737c478bd9Sstevel@tonic-gate } 3747c478bd9Sstevel@tonic-gate if (/\s$/) { 3757c478bd9Sstevel@tonic-gate err("space or tab at end of line"); 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate if (/[^ \t(]\/\*/ && !/\w\(\/\*.*\*\/\);/) { 3787c478bd9Sstevel@tonic-gate err("comment preceded by non-blank"); 3797c478bd9Sstevel@tonic-gate } 3807c478bd9Sstevel@tonic-gate 3817c478bd9Sstevel@tonic-gate # is this the beginning or ending of a function? 3827c478bd9Sstevel@tonic-gate # (not if "struct foo\n{\n") 3837c478bd9Sstevel@tonic-gate if (/^{$/ && $prev =~ /\)\s*(const\s*)?(\/\*.*\*\/\s*)?\\?$/) { 3847c478bd9Sstevel@tonic-gate $in_function = 1; 3857c478bd9Sstevel@tonic-gate $in_declaration = 1; 3867c478bd9Sstevel@tonic-gate $in_function_header = 0; 3877c478bd9Sstevel@tonic-gate $prev = $line; 3887c478bd9Sstevel@tonic-gate next line; 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate if (/^}\s*(\/\*.*\*\/\s*)*$/) { 3917c478bd9Sstevel@tonic-gate if ($prev =~ /^\s*return\s*;/) { 3927c478bd9Sstevel@tonic-gate err_prev("unneeded return at end of function"); 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate $in_function = 0; 3957c478bd9Sstevel@tonic-gate reset_indent(); # we don't check between functions 3967c478bd9Sstevel@tonic-gate $prev = $line; 3977c478bd9Sstevel@tonic-gate next line; 3987c478bd9Sstevel@tonic-gate } 3997c478bd9Sstevel@tonic-gate if (/^\w*\($/) { 4007c478bd9Sstevel@tonic-gate $in_function_header = 1; 4017c478bd9Sstevel@tonic-gate } 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate if ($in_warlock_comment && /\*\//) { 4047c478bd9Sstevel@tonic-gate $in_warlock_comment = 0; 4057c478bd9Sstevel@tonic-gate $prev = $line; 4067c478bd9Sstevel@tonic-gate next line; 4077c478bd9Sstevel@tonic-gate } 4087c478bd9Sstevel@tonic-gate 4097c478bd9Sstevel@tonic-gate # a blank line terminates the declarations within a function. 4107c478bd9Sstevel@tonic-gate # XXX - but still a problem in sub-blocks. 4117c478bd9Sstevel@tonic-gate if ($in_declaration && /^$/) { 4127c478bd9Sstevel@tonic-gate $in_declaration = 0; 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate if ($comment_done) { 4167c478bd9Sstevel@tonic-gate $in_comment = 0; 4177c478bd9Sstevel@tonic-gate $in_header_comment = 0; 4187c478bd9Sstevel@tonic-gate $comment_done = 0; 4197c478bd9Sstevel@tonic-gate } 4207c478bd9Sstevel@tonic-gate # does this looks like the start of a block comment? 4213c2f5c48Sjwadams if (/$hdr_comment_start/) { 4223c2f5c48Sjwadams if (!/^\t*\/\*/) { 4237c478bd9Sstevel@tonic-gate err("block comment not indented by tabs"); 4247c478bd9Sstevel@tonic-gate } 4257c478bd9Sstevel@tonic-gate $in_comment = 1; 4263c2f5c48Sjwadams /^(\s*)\//; 4273c2f5c48Sjwadams $comment_prefix = $1; 4287c478bd9Sstevel@tonic-gate if ($comment_prefix eq "") { 4297c478bd9Sstevel@tonic-gate $in_header_comment = 1; 4307c478bd9Sstevel@tonic-gate } 4317c478bd9Sstevel@tonic-gate $prev = $line; 4327c478bd9Sstevel@tonic-gate next line; 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate # are we still in the block comment? 4357c478bd9Sstevel@tonic-gate if ($in_comment) { 4367c478bd9Sstevel@tonic-gate if (/^$comment_prefix \*\/$/) { 4377c478bd9Sstevel@tonic-gate $comment_done = 1; 4387c478bd9Sstevel@tonic-gate } elsif (/\*\//) { 4397c478bd9Sstevel@tonic-gate $comment_done = 1; 4407c478bd9Sstevel@tonic-gate err("improper block comment close") 4417c478bd9Sstevel@tonic-gate unless ($ignore_hdr_comment && $in_header_comment); 4427c478bd9Sstevel@tonic-gate } elsif (!/^$comment_prefix \*[ \t]/ && 4437c478bd9Sstevel@tonic-gate !/^$comment_prefix \*$/) { 4447c478bd9Sstevel@tonic-gate err("improper block comment") 4457c478bd9Sstevel@tonic-gate unless ($ignore_hdr_comment && $in_header_comment); 4467c478bd9Sstevel@tonic-gate } 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate if ($in_header_comment && $ignore_hdr_comment) { 4507c478bd9Sstevel@tonic-gate $prev = $line; 4517c478bd9Sstevel@tonic-gate next line; 4527c478bd9Sstevel@tonic-gate } 4537c478bd9Sstevel@tonic-gate 4547c478bd9Sstevel@tonic-gate # check for errors that might occur in comments and in code. 4557c478bd9Sstevel@tonic-gate 4567c478bd9Sstevel@tonic-gate # allow spaces to be used to draw pictures in header comments. 4577c478bd9Sstevel@tonic-gate if (/[^ ] / && !/".* .*"/ && !$in_header_comment) { 4587c478bd9Sstevel@tonic-gate err("spaces instead of tabs"); 4597c478bd9Sstevel@tonic-gate } 4607c478bd9Sstevel@tonic-gate if (/^ / && !/^ \*[ \t\/]/ && !/^ \*$/ && 4617c478bd9Sstevel@tonic-gate (!/^ \w/ || $in_function != 0)) { 4627c478bd9Sstevel@tonic-gate err("indent by spaces instead of tabs"); 4637c478bd9Sstevel@tonic-gate } 4647c478bd9Sstevel@tonic-gate if (/^\t+ [^ \t\*]/ || /^\t+ \S/ || /^\t+ \S/) { 4657c478bd9Sstevel@tonic-gate err("continuation line not indented by 4 spaces"); 4667c478bd9Sstevel@tonic-gate } 4673c2f5c48Sjwadams if (/$warlock_re/ && !/\*\//) { 4687c478bd9Sstevel@tonic-gate $in_warlock_comment = 1; 4697c478bd9Sstevel@tonic-gate $prev = $line; 4707c478bd9Sstevel@tonic-gate next line; 4717c478bd9Sstevel@tonic-gate } 4723c2f5c48Sjwadams if (/^\s*\/\*./ && !/^\s*\/\*.*\*\// && !/$hdr_comment_start/) { 4737c478bd9Sstevel@tonic-gate err("improper first line of block comment"); 4747c478bd9Sstevel@tonic-gate } 4757c478bd9Sstevel@tonic-gate 4767c478bd9Sstevel@tonic-gate if ($in_comment) { # still in comment, don't do further checks 4777c478bd9Sstevel@tonic-gate $prev = $line; 4787c478bd9Sstevel@tonic-gate next line; 4797c478bd9Sstevel@tonic-gate } 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate if ((/[^(]\/\*\S/ || /^\/\*\S/) && 4823c2f5c48Sjwadams !(/$lint_re/ || ($splint_comments && /$splint_re/))) { 4837c478bd9Sstevel@tonic-gate err("missing blank after open comment"); 4847c478bd9Sstevel@tonic-gate } 4857c478bd9Sstevel@tonic-gate if (/\S\*\/[^)]|\S\*\/$/ && 4863c2f5c48Sjwadams !(/$lint_re/ || ($splint_comments && /$splint_re/))) { 4877c478bd9Sstevel@tonic-gate err("missing blank before close comment"); 4887c478bd9Sstevel@tonic-gate } 4897c478bd9Sstevel@tonic-gate if (/\/\/\S/) { # C++ comments 4907c478bd9Sstevel@tonic-gate err("missing blank after start comment"); 4917c478bd9Sstevel@tonic-gate } 4927c478bd9Sstevel@tonic-gate # check for unterminated single line comments, but allow them when 4937c478bd9Sstevel@tonic-gate # they are used to comment out the argument list of a function 4947c478bd9Sstevel@tonic-gate # declaration. 4957c478bd9Sstevel@tonic-gate if (/\S.*\/\*/ && !/\S.*\/\*.*\*\// && !/\(\/\*/) { 4967c478bd9Sstevel@tonic-gate err("unterminated single line comment"); 4977c478bd9Sstevel@tonic-gate } 4987c478bd9Sstevel@tonic-gate 4997c478bd9Sstevel@tonic-gate if (/^(#else|#endif|#include)(.*)$/) { 5007c478bd9Sstevel@tonic-gate $prev = $line; 5017c478bd9Sstevel@tonic-gate if ($picky) { 5027c478bd9Sstevel@tonic-gate my $directive = $1; 5037c478bd9Sstevel@tonic-gate my $clause = $2; 5047c478bd9Sstevel@tonic-gate # Enforce ANSI rules for #else and #endif: no noncomment 5057c478bd9Sstevel@tonic-gate # identifiers are allowed after #endif or #else. Allow 5067c478bd9Sstevel@tonic-gate # C++ comments since they seem to be a fact of life. 5077c478bd9Sstevel@tonic-gate if ((($1 eq "#endif") || ($1 eq "#else")) && 5087c478bd9Sstevel@tonic-gate ($clause ne "") && 5097c478bd9Sstevel@tonic-gate (!($clause =~ /^\s+\/\*.*\*\/$/)) && 5107c478bd9Sstevel@tonic-gate (!($clause =~ /^\s+\/\/.*$/))) { 5117c478bd9Sstevel@tonic-gate err("non-comment text following " . 5127c478bd9Sstevel@tonic-gate "$directive (or malformed $directive " . 5137c478bd9Sstevel@tonic-gate "directive)"); 5147c478bd9Sstevel@tonic-gate } 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate next line; 5177c478bd9Sstevel@tonic-gate } 5187c478bd9Sstevel@tonic-gate 5197c478bd9Sstevel@tonic-gate # 5207c478bd9Sstevel@tonic-gate # delete any comments and check everything else. Note that 5217c478bd9Sstevel@tonic-gate # ".*?" is a non-greedy match, so that we don't get confused by 5227c478bd9Sstevel@tonic-gate # multiple comments on the same line. 5237c478bd9Sstevel@tonic-gate # 5247c478bd9Sstevel@tonic-gate s/\/\*.*?\*\///g; 5257c478bd9Sstevel@tonic-gate s/\/\/.*$//; # C++ comments 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate # delete any trailing whitespace; we have already checked for that. 5287c478bd9Sstevel@tonic-gate s/\s*$//; 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate # following checks do not apply to text in comments. 5317c478bd9Sstevel@tonic-gate 5327c478bd9Sstevel@tonic-gate if (/[^<>\s][!<>=]=/ || /[^<>][!<>=]=[^\s,]/ || 5337c478bd9Sstevel@tonic-gate (/[^->]>[^,=>\s]/ && !/[^->]>$/) || 5347c478bd9Sstevel@tonic-gate (/[^<]<[^,=<\s]/ && !/[^<]<$/) || 5357c478bd9Sstevel@tonic-gate /[^<\s]<[^<]/ || /[^->\s]>[^>]/) { 5367c478bd9Sstevel@tonic-gate err("missing space around relational operator"); 5377c478bd9Sstevel@tonic-gate } 5387c478bd9Sstevel@tonic-gate if (/\S>>=/ || /\S<<=/ || />>=\S/ || /<<=\S/ || /\S[-+*\/&|^%]=/ || 5397c478bd9Sstevel@tonic-gate (/[^-+*\/&|^%!<>=\s]=[^=]/ && !/[^-+*\/&|^%!<>=\s]=$/) || 5407c478bd9Sstevel@tonic-gate (/[^!<>=]=[^=\s]/ && !/[^!<>=]=$/)) { 5417c478bd9Sstevel@tonic-gate # XXX - should only check this for C++ code 5427c478bd9Sstevel@tonic-gate # XXX - there are probably other forms that should be allowed 5437c478bd9Sstevel@tonic-gate if (!/\soperator=/) { 5447c478bd9Sstevel@tonic-gate err("missing space around assignment operator"); 5457c478bd9Sstevel@tonic-gate } 5467c478bd9Sstevel@tonic-gate } 5477c478bd9Sstevel@tonic-gate if (/[,;]\S/ && !/\bfor \(;;\)/) { 5487c478bd9Sstevel@tonic-gate err("comma or semicolon followed by non-blank"); 5497c478bd9Sstevel@tonic-gate } 5507c478bd9Sstevel@tonic-gate # allow "for" statements to have empty "while" clauses 5517c478bd9Sstevel@tonic-gate if (/\s[,;]/ && !/^[\t]+;$/ && !/^\s*for \([^;]*; ;[^;]*\)/) { 5527c478bd9Sstevel@tonic-gate err("comma or semicolon preceded by blank"); 5537c478bd9Sstevel@tonic-gate } 5547c478bd9Sstevel@tonic-gate if (/^\s*(&&|\|\|)/) { 5557c478bd9Sstevel@tonic-gate err("improper boolean continuation"); 5567c478bd9Sstevel@tonic-gate } 5577c478bd9Sstevel@tonic-gate if (/\S *(&&|\|\|)/ || /(&&|\|\|) *\S/) { 5587c478bd9Sstevel@tonic-gate err("more than one space around boolean operator"); 5597c478bd9Sstevel@tonic-gate } 5607c478bd9Sstevel@tonic-gate if (/\b(for|if|while|switch|sizeof|return|case)\(/) { 5617c478bd9Sstevel@tonic-gate err("missing space between keyword and paren"); 5627c478bd9Sstevel@tonic-gate } 5637c478bd9Sstevel@tonic-gate if (/(\b(for|if|while|switch|return)\b.*){2,}/ && !/^#define/) { 5647c478bd9Sstevel@tonic-gate # multiple "case" and "sizeof" allowed 5657c478bd9Sstevel@tonic-gate err("more than one keyword on line"); 5667c478bd9Sstevel@tonic-gate } 5677c478bd9Sstevel@tonic-gate if (/\b(for|if|while|switch|sizeof|return|case)\s\s+\(/ && 5687c478bd9Sstevel@tonic-gate !/^#if\s+\(/) { 5697c478bd9Sstevel@tonic-gate err("extra space between keyword and paren"); 5707c478bd9Sstevel@tonic-gate } 5717c478bd9Sstevel@tonic-gate # try to detect "func (x)" but not "if (x)" or 5727c478bd9Sstevel@tonic-gate # "#define foo (x)" or "int (*func)();" 5737c478bd9Sstevel@tonic-gate if (/\w\s\(/) { 5747c478bd9Sstevel@tonic-gate my $s = $_; 5757c478bd9Sstevel@tonic-gate # strip off all keywords on the line 5767c478bd9Sstevel@tonic-gate s/\b(for|if|while|switch|return|case|sizeof)\s\(/XXX(/g; 5777c478bd9Sstevel@tonic-gate s/#elif\s\(/XXX(/g; 5787c478bd9Sstevel@tonic-gate s/^#define\s+\w+\s+\(/XXX(/; 5797c478bd9Sstevel@tonic-gate # do not match things like "void (*f)();" 5807c478bd9Sstevel@tonic-gate # or "typedef void (func_t)();" 5817c478bd9Sstevel@tonic-gate s/\w\s\(+\*/XXX(*/g; 5827c478bd9Sstevel@tonic-gate s/\b($typename|void)\s+\(+/XXX(/og; 5837c478bd9Sstevel@tonic-gate if (/\w\s\(/) { 5847c478bd9Sstevel@tonic-gate err("extra space between function name and left paren"); 5857c478bd9Sstevel@tonic-gate } 5867c478bd9Sstevel@tonic-gate $_ = $s; 5877c478bd9Sstevel@tonic-gate } 5887c478bd9Sstevel@tonic-gate # try to detect "int foo(x)", but not "extern int foo(x);" 5897c478bd9Sstevel@tonic-gate # XXX - this still trips over too many legitimate things, 5907c478bd9Sstevel@tonic-gate # like "int foo(x,\n\ty);" 5917c478bd9Sstevel@tonic-gate# if (/^(\w+(\s|\*)+)+\w+\(/ && !/\)[;,](\s|)*$/ && 5927c478bd9Sstevel@tonic-gate# !/^(extern|static)\b/) { 5937c478bd9Sstevel@tonic-gate# err("return type of function not on separate line"); 5947c478bd9Sstevel@tonic-gate# } 5957c478bd9Sstevel@tonic-gate # this is a close approximation 5967c478bd9Sstevel@tonic-gate if (/^(\w+(\s|\*)+)+\w+\(.*\)(\s|)*$/ && 5977c478bd9Sstevel@tonic-gate !/^(extern|static)\b/) { 5987c478bd9Sstevel@tonic-gate err("return type of function not on separate line"); 5997c478bd9Sstevel@tonic-gate } 6007c478bd9Sstevel@tonic-gate if (/^#define /) { 6017c478bd9Sstevel@tonic-gate err("#define followed by space instead of tab"); 6027c478bd9Sstevel@tonic-gate } 6037c478bd9Sstevel@tonic-gate if (/^\s*return\W[^;]*;/ && !/^\s*return\s*\(.*\);/) { 6047c478bd9Sstevel@tonic-gate err("unparenthesized return expression"); 6057c478bd9Sstevel@tonic-gate } 6067c478bd9Sstevel@tonic-gate if (/\bsizeof\b/ && !/\bsizeof\s*\(.*\)/) { 6077c478bd9Sstevel@tonic-gate err("unparenthesized sizeof expression"); 6087c478bd9Sstevel@tonic-gate } 6097c478bd9Sstevel@tonic-gate if (/\(\s/) { 6107c478bd9Sstevel@tonic-gate err("whitespace after left paren"); 6117c478bd9Sstevel@tonic-gate } 6127c478bd9Sstevel@tonic-gate # allow "for" statements to have empty "continue" clauses 6137c478bd9Sstevel@tonic-gate if (/\s\)/ && !/^\s*for \([^;]*;[^;]*; \)/) { 6147c478bd9Sstevel@tonic-gate err("whitespace before right paren"); 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate if (/^\s*\(void\)[^ ]/) { 6177c478bd9Sstevel@tonic-gate err("missing space after (void) cast"); 6187c478bd9Sstevel@tonic-gate } 619*94385aa4SToomas Soome if (/\S\{/ && !/\{\{/) { 6207c478bd9Sstevel@tonic-gate err("missing space before left brace"); 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate if ($in_function && /^\s+{/ && 6237c478bd9Sstevel@tonic-gate ($prev =~ /\)\s*$/ || $prev =~ /\bstruct\s+\w+$/)) { 6247c478bd9Sstevel@tonic-gate err("left brace starting a line"); 6257c478bd9Sstevel@tonic-gate } 6267c478bd9Sstevel@tonic-gate if (/}(else|while)/) { 6277c478bd9Sstevel@tonic-gate err("missing space after right brace"); 6287c478bd9Sstevel@tonic-gate } 6297c478bd9Sstevel@tonic-gate if (/}\s\s+(else|while)/) { 6307c478bd9Sstevel@tonic-gate err("extra space after right brace"); 6317c478bd9Sstevel@tonic-gate } 6327c478bd9Sstevel@tonic-gate if (/\b_VOID\b|\bVOID\b|\bSTATIC\b/) { 6337c478bd9Sstevel@tonic-gate err("obsolete use of VOID or STATIC"); 6347c478bd9Sstevel@tonic-gate } 6357c478bd9Sstevel@tonic-gate if (/\b$typename\*/o) { 6367c478bd9Sstevel@tonic-gate err("missing space between type name and *"); 6377c478bd9Sstevel@tonic-gate } 6387c478bd9Sstevel@tonic-gate if (/^\s+#/) { 6397c478bd9Sstevel@tonic-gate err("preprocessor statement not in column 1"); 6407c478bd9Sstevel@tonic-gate } 6417c478bd9Sstevel@tonic-gate if (/^#\s/) { 6427c478bd9Sstevel@tonic-gate err("blank after preprocessor #"); 6437c478bd9Sstevel@tonic-gate } 6447c478bd9Sstevel@tonic-gate if (/!\s*(strcmp|strncmp|bcmp)\s*\(/) { 6457c478bd9Sstevel@tonic-gate err("don't use boolean ! with comparison functions"); 6467c478bd9Sstevel@tonic-gate } 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate # 6497c478bd9Sstevel@tonic-gate # We completely ignore, for purposes of indentation: 6507c478bd9Sstevel@tonic-gate # * lines outside of functions 6517c478bd9Sstevel@tonic-gate # * preprocessor lines 6527c478bd9Sstevel@tonic-gate # 6537c478bd9Sstevel@tonic-gate if ($check_continuation && $in_function && !$in_cpp) { 6547c478bd9Sstevel@tonic-gate process_indent($_); 6557c478bd9Sstevel@tonic-gate } 6567c478bd9Sstevel@tonic-gate if ($picky) { 6577c478bd9Sstevel@tonic-gate # try to detect spaces after casts, but allow (e.g.) 6583c2f5c48Sjwadams # "sizeof (int) + 1", "void (*funcptr)(int) = foo;", and 6593c2f5c48Sjwadams # "int foo(int) __NORETURN;" 6603c2f5c48Sjwadams if ((/^\($typename( \*+)?\)\s/o || 6613c2f5c48Sjwadams /\W\($typename( \*+)?\)\s/o) && 6627c478bd9Sstevel@tonic-gate !/sizeof\s*\($typename( \*)?\)\s/o && 6637c478bd9Sstevel@tonic-gate !/\($typename( \*+)?\)\s+=[^=]/o) { 6647c478bd9Sstevel@tonic-gate err("space after cast"); 6657c478bd9Sstevel@tonic-gate } 6667c478bd9Sstevel@tonic-gate if (/\b$typename\s*\*\s/o && 6677c478bd9Sstevel@tonic-gate !/\b$typename\s*\*\s+const\b/o) { 6687c478bd9Sstevel@tonic-gate err("unary * followed by space"); 6697c478bd9Sstevel@tonic-gate } 6707c478bd9Sstevel@tonic-gate } 6717c478bd9Sstevel@tonic-gate if ($check_posix_types) { 6727c478bd9Sstevel@tonic-gate # try to detect old non-POSIX types. 6737c478bd9Sstevel@tonic-gate # POSIX requires all non-standard typedefs to end in _t, 6747c478bd9Sstevel@tonic-gate # but historically these have been used. 6757c478bd9Sstevel@tonic-gate if (/\b(unchar|ushort|uint|ulong|u_int|u_short|u_long|u_char|quad)\b/) { 6767c478bd9Sstevel@tonic-gate err("non-POSIX typedef $1 used: use $old2posix{$1} instead"); 6777c478bd9Sstevel@tonic-gate } 6787c478bd9Sstevel@tonic-gate } 6797c478bd9Sstevel@tonic-gate if ($heuristic) { 6807c478bd9Sstevel@tonic-gate # cannot check this everywhere due to "struct {\n...\n} foo;" 6817c478bd9Sstevel@tonic-gate if ($in_function && !$in_declaration && 6827c478bd9Sstevel@tonic-gate /}./ && !/}\s+=/ && !/{.*}[;,]$/ && !/}(\s|)*$/ && 6837c478bd9Sstevel@tonic-gate !/} (else|while)/ && !/}}/) { 6847c478bd9Sstevel@tonic-gate err("possible bad text following right brace"); 6857c478bd9Sstevel@tonic-gate } 6867c478bd9Sstevel@tonic-gate # cannot check this because sub-blocks in 6877c478bd9Sstevel@tonic-gate # the middle of code are ok 6887c478bd9Sstevel@tonic-gate if ($in_function && /^\s+{/) { 6897c478bd9Sstevel@tonic-gate err("possible left brace starting a line"); 6907c478bd9Sstevel@tonic-gate } 6917c478bd9Sstevel@tonic-gate } 6927c478bd9Sstevel@tonic-gate if (/^\s*else\W/) { 6937c478bd9Sstevel@tonic-gate if ($prev =~ /^\s*}$/) { 6947c478bd9Sstevel@tonic-gate err_prefix($prev, 6957c478bd9Sstevel@tonic-gate "else and right brace should be on same line"); 6967c478bd9Sstevel@tonic-gate } 6977c478bd9Sstevel@tonic-gate } 6987c478bd9Sstevel@tonic-gate $prev = $line; 6997c478bd9Sstevel@tonic-gate} 7007c478bd9Sstevel@tonic-gate 7017c478bd9Sstevel@tonic-gateif ($prev eq "") { 7027c478bd9Sstevel@tonic-gate err("last line in file is blank"); 7037c478bd9Sstevel@tonic-gate} 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate} 7067c478bd9Sstevel@tonic-gate 7077c478bd9Sstevel@tonic-gate# 7087c478bd9Sstevel@tonic-gate# Continuation-line checking 7097c478bd9Sstevel@tonic-gate# 7107c478bd9Sstevel@tonic-gate# The rest of this file contains the code for the continuation checking 7117c478bd9Sstevel@tonic-gate# engine. It's a pretty simple state machine which tracks the expression 7127c478bd9Sstevel@tonic-gate# depth (unmatched '('s and '['s). 7137c478bd9Sstevel@tonic-gate# 7147c478bd9Sstevel@tonic-gate# Keep in mind that the argument to process_indent() has already been heavily 7157c478bd9Sstevel@tonic-gate# processed; all comments have been replaced by control-A, and the contents of 7167c478bd9Sstevel@tonic-gate# strings and character constants have been elided. 7177c478bd9Sstevel@tonic-gate# 7187c478bd9Sstevel@tonic-gate 7197c478bd9Sstevel@tonic-gatemy $cont_in; # currently inside of a continuation 7207c478bd9Sstevel@tonic-gatemy $cont_off; # skipping an initializer or definition 7217c478bd9Sstevel@tonic-gatemy $cont_noerr; # suppress cascading errors 7227c478bd9Sstevel@tonic-gatemy $cont_start; # the line being continued 7237c478bd9Sstevel@tonic-gatemy $cont_base; # the base indentation 7247c478bd9Sstevel@tonic-gatemy $cont_first; # this is the first line of a statement 7257c478bd9Sstevel@tonic-gatemy $cont_multiseg; # this continuation has multiple segments 7267c478bd9Sstevel@tonic-gate 7277c478bd9Sstevel@tonic-gatemy $cont_special; # this is a C statement (if, for, etc.) 7287c478bd9Sstevel@tonic-gatemy $cont_macro; # this is a macro 7297c478bd9Sstevel@tonic-gatemy $cont_case; # this is a multi-line case 7307c478bd9Sstevel@tonic-gate 7317c478bd9Sstevel@tonic-gatemy @cont_paren; # the stack of unmatched ( and [s we've seen 7327c478bd9Sstevel@tonic-gate 7337c478bd9Sstevel@tonic-gatesub 7347c478bd9Sstevel@tonic-gatereset_indent() 7357c478bd9Sstevel@tonic-gate{ 7367c478bd9Sstevel@tonic-gate $cont_in = 0; 7377c478bd9Sstevel@tonic-gate $cont_off = 0; 7387c478bd9Sstevel@tonic-gate} 7397c478bd9Sstevel@tonic-gate 7407c478bd9Sstevel@tonic-gatesub 7417c478bd9Sstevel@tonic-gatedelabel($) 7427c478bd9Sstevel@tonic-gate{ 7437c478bd9Sstevel@tonic-gate # 7447c478bd9Sstevel@tonic-gate # replace labels with tabs. Note that there may be multiple 7457c478bd9Sstevel@tonic-gate # labels on a line. 7467c478bd9Sstevel@tonic-gate # 7477c478bd9Sstevel@tonic-gate local $_ = $_[0]; 7487c478bd9Sstevel@tonic-gate 7497c478bd9Sstevel@tonic-gate while (/^(\t*)( *(?:(?:\w+\s*)|(?:case\b[^:]*)): *)(.*)$/) { 7507c478bd9Sstevel@tonic-gate my ($pre_tabs, $label, $rest) = ($1, $2, $3); 7517c478bd9Sstevel@tonic-gate $_ = $pre_tabs; 7527c478bd9Sstevel@tonic-gate while ($label =~ s/^([^\t]*)(\t+)//) { 7537c478bd9Sstevel@tonic-gate $_ .= "\t" x (length($2) + length($1) / 8); 7547c478bd9Sstevel@tonic-gate } 7557c478bd9Sstevel@tonic-gate $_ .= ("\t" x (length($label) / 8)).$rest; 7567c478bd9Sstevel@tonic-gate } 7577c478bd9Sstevel@tonic-gate 7587c478bd9Sstevel@tonic-gate return ($_); 7597c478bd9Sstevel@tonic-gate} 7607c478bd9Sstevel@tonic-gate 7617c478bd9Sstevel@tonic-gatesub 7627c478bd9Sstevel@tonic-gateprocess_indent($) 7637c478bd9Sstevel@tonic-gate{ 7647c478bd9Sstevel@tonic-gate require strict; 7657c478bd9Sstevel@tonic-gate local $_ = $_[0]; # preserve the global $_ 7667c478bd9Sstevel@tonic-gate 7677c478bd9Sstevel@tonic-gate s///g; # No comments 7687c478bd9Sstevel@tonic-gate s/\s+$//; # Strip trailing whitespace 7697c478bd9Sstevel@tonic-gate 7707c478bd9Sstevel@tonic-gate return if (/^$/); # skip empty lines 7717c478bd9Sstevel@tonic-gate 7727c478bd9Sstevel@tonic-gate # regexps used below; keywords taking (), macros, and continued cases 7737c478bd9Sstevel@tonic-gate my $special = '(?:(?:\}\s*)?else\s+)?(?:if|for|while|switch)\b'; 7747c478bd9Sstevel@tonic-gate my $macro = '[A-Z_][A-Z_0-9]*\('; 7757c478bd9Sstevel@tonic-gate my $case = 'case\b[^:]*$'; 7767c478bd9Sstevel@tonic-gate 7777c478bd9Sstevel@tonic-gate # skip over enumerations, array definitions, initializers, etc. 7787c478bd9Sstevel@tonic-gate if ($cont_off <= 0 && !/^\s*$special/ && 7797c478bd9Sstevel@tonic-gate (/(?:(?:\b(?:enum|struct|union)\s*[^\{]*)|(?:\s+=\s*)){/ || 7807c478bd9Sstevel@tonic-gate (/^\s*{/ && $prev =~ /=\s*(?:\/\*.*\*\/\s*)*$/))) { 7817c478bd9Sstevel@tonic-gate $cont_in = 0; 7827c478bd9Sstevel@tonic-gate $cont_off = tr/{/{/ - tr/}/}/; 7837c478bd9Sstevel@tonic-gate return; 7847c478bd9Sstevel@tonic-gate } 7857c478bd9Sstevel@tonic-gate if ($cont_off) { 7867c478bd9Sstevel@tonic-gate $cont_off += tr/{/{/ - tr/}/}/; 7877c478bd9Sstevel@tonic-gate return; 7887c478bd9Sstevel@tonic-gate } 7897c478bd9Sstevel@tonic-gate 7907c478bd9Sstevel@tonic-gate if (!$cont_in) { 7917c478bd9Sstevel@tonic-gate $cont_start = $line; 7927c478bd9Sstevel@tonic-gate 7937c478bd9Sstevel@tonic-gate if (/^\t* /) { 7947c478bd9Sstevel@tonic-gate err("non-continuation indented 4 spaces"); 7957c478bd9Sstevel@tonic-gate $cont_noerr = 1; # stop reporting 7967c478bd9Sstevel@tonic-gate } 7977c478bd9Sstevel@tonic-gate $_ = delabel($_); # replace labels with tabs 7987c478bd9Sstevel@tonic-gate 7997c478bd9Sstevel@tonic-gate # check if the statement is complete 8007c478bd9Sstevel@tonic-gate return if (/^\s*\}?$/); 8017c478bd9Sstevel@tonic-gate return if (/^\s*\}?\s*else\s*\{?$/); 8027c478bd9Sstevel@tonic-gate return if (/^\s*do\s*\{?$/); 8037c478bd9Sstevel@tonic-gate return if (/{$/); 8047c478bd9Sstevel@tonic-gate return if (/}[,;]?$/); 8057c478bd9Sstevel@tonic-gate 8066f55da5aSjwadams # Allow macros on their own lines 8076f55da5aSjwadams return if (/^\s*[A-Z_][A-Z_0-9]*$/); 8086f55da5aSjwadams 8097c478bd9Sstevel@tonic-gate # cases we don't deal with, generally non-kosher 8107c478bd9Sstevel@tonic-gate if (/{/) { 8117c478bd9Sstevel@tonic-gate err("stuff after {"); 8127c478bd9Sstevel@tonic-gate return; 8137c478bd9Sstevel@tonic-gate } 8147c478bd9Sstevel@tonic-gate 8157c478bd9Sstevel@tonic-gate # Get the base line, and set up the state machine 8167c478bd9Sstevel@tonic-gate /^(\t*)/; 8177c478bd9Sstevel@tonic-gate $cont_base = $1; 8187c478bd9Sstevel@tonic-gate $cont_in = 1; 8197c478bd9Sstevel@tonic-gate @cont_paren = (); 8207c478bd9Sstevel@tonic-gate $cont_first = 1; 8217c478bd9Sstevel@tonic-gate $cont_multiseg = 0; 8227c478bd9Sstevel@tonic-gate 8237c478bd9Sstevel@tonic-gate # certain things need special processing 8247c478bd9Sstevel@tonic-gate $cont_special = /^\s*$special/? 1 : 0; 8257c478bd9Sstevel@tonic-gate $cont_macro = /^\s*$macro/? 1 : 0; 8267c478bd9Sstevel@tonic-gate $cont_case = /^\s*$case/? 1 : 0; 8277c478bd9Sstevel@tonic-gate } else { 8287c478bd9Sstevel@tonic-gate $cont_first = 0; 8297c478bd9Sstevel@tonic-gate 8307c478bd9Sstevel@tonic-gate # Strings may be pulled back to an earlier (half-)tabstop 8317c478bd9Sstevel@tonic-gate unless ($cont_noerr || /^$cont_base / || 8327c478bd9Sstevel@tonic-gate (/^\t*(?: )?(?:gettext\()?\"/ && !/^$cont_base\t/)) { 8337c478bd9Sstevel@tonic-gate err_prefix($cont_start, 8347c478bd9Sstevel@tonic-gate "continuation should be indented 4 spaces"); 8357c478bd9Sstevel@tonic-gate } 8367c478bd9Sstevel@tonic-gate } 8377c478bd9Sstevel@tonic-gate 8387c478bd9Sstevel@tonic-gate my $rest = $_; # keeps the remainder of the line 8397c478bd9Sstevel@tonic-gate 8407c478bd9Sstevel@tonic-gate # 8417c478bd9Sstevel@tonic-gate # The split matches 0 characters, so that each 'special' character 8427c478bd9Sstevel@tonic-gate # is processed separately. Parens and brackets are pushed and 8437c478bd9Sstevel@tonic-gate # popped off the @cont_paren stack. For normal processing, we wait 8447c478bd9Sstevel@tonic-gate # until a ; or { terminates the statement. "special" processing 8457c478bd9Sstevel@tonic-gate # (if/for/while/switch) is allowed to stop when the stack empties, 8467c478bd9Sstevel@tonic-gate # as is macro processing. Case statements are terminated with a : 8477c478bd9Sstevel@tonic-gate # and an empty paren stack. 8487c478bd9Sstevel@tonic-gate # 8497c478bd9Sstevel@tonic-gate foreach $_ (split /[^\(\)\[\]\{\}\;\:]*/) { 8507c478bd9Sstevel@tonic-gate next if (length($_) == 0); 8517c478bd9Sstevel@tonic-gate 8527c478bd9Sstevel@tonic-gate # rest contains the remainder of the line 8537c478bd9Sstevel@tonic-gate my $rxp = "[^\Q$_\E]*\Q$_\E"; 8547c478bd9Sstevel@tonic-gate $rest =~ s/^$rxp//; 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate if (/\(/ || /\[/) { 8577c478bd9Sstevel@tonic-gate push @cont_paren, $_; 8587c478bd9Sstevel@tonic-gate } elsif (/\)/ || /\]/) { 8597c478bd9Sstevel@tonic-gate my $cur = $_; 8607c478bd9Sstevel@tonic-gate tr/\)\]/\(\[/; 8617c478bd9Sstevel@tonic-gate 8627c478bd9Sstevel@tonic-gate my $old = (pop @cont_paren); 8637c478bd9Sstevel@tonic-gate if (!defined($old)) { 8647c478bd9Sstevel@tonic-gate err("unexpected '$cur'"); 8657c478bd9Sstevel@tonic-gate $cont_in = 0; 8667c478bd9Sstevel@tonic-gate last; 8677c478bd9Sstevel@tonic-gate } elsif ($old ne $_) { 8687c478bd9Sstevel@tonic-gate err("'$cur' mismatched with '$old'"); 8697c478bd9Sstevel@tonic-gate $cont_in = 0; 8707c478bd9Sstevel@tonic-gate last; 8717c478bd9Sstevel@tonic-gate } 8727c478bd9Sstevel@tonic-gate 8737c478bd9Sstevel@tonic-gate # 8747c478bd9Sstevel@tonic-gate # If the stack is now empty, do special processing 8757c478bd9Sstevel@tonic-gate # for if/for/while/switch and macro statements. 8767c478bd9Sstevel@tonic-gate # 8777c478bd9Sstevel@tonic-gate next if (@cont_paren != 0); 8787c478bd9Sstevel@tonic-gate if ($cont_special) { 8797c478bd9Sstevel@tonic-gate if ($rest =~ /^\s*{?$/) { 8807c478bd9Sstevel@tonic-gate $cont_in = 0; 8817c478bd9Sstevel@tonic-gate last; 8827c478bd9Sstevel@tonic-gate } 8837c478bd9Sstevel@tonic-gate if ($rest =~ /^\s*;$/) { 8847c478bd9Sstevel@tonic-gate err("empty if/for/while body ". 8857c478bd9Sstevel@tonic-gate "not on its own line"); 8867c478bd9Sstevel@tonic-gate $cont_in = 0; 8877c478bd9Sstevel@tonic-gate last; 8887c478bd9Sstevel@tonic-gate } 8897c478bd9Sstevel@tonic-gate if (!$cont_first && $cont_multiseg == 1) { 8907c478bd9Sstevel@tonic-gate err_prefix($cont_start, 8917c478bd9Sstevel@tonic-gate "multiple statements continued ". 8927c478bd9Sstevel@tonic-gate "over multiple lines"); 8937c478bd9Sstevel@tonic-gate $cont_multiseg = 2; 8947c478bd9Sstevel@tonic-gate } elsif ($cont_multiseg == 0) { 8957c478bd9Sstevel@tonic-gate $cont_multiseg = 1; 8967c478bd9Sstevel@tonic-gate } 8977c478bd9Sstevel@tonic-gate # We've finished this section, start 8987c478bd9Sstevel@tonic-gate # processing the next. 8997c478bd9Sstevel@tonic-gate goto section_ended; 9007c478bd9Sstevel@tonic-gate } 9017c478bd9Sstevel@tonic-gate if ($cont_macro) { 9027c478bd9Sstevel@tonic-gate if ($rest =~ /^$/) { 9037c478bd9Sstevel@tonic-gate $cont_in = 0; 9047c478bd9Sstevel@tonic-gate last; 9057c478bd9Sstevel@tonic-gate } 9067c478bd9Sstevel@tonic-gate } 9077c478bd9Sstevel@tonic-gate } elsif (/\;/) { 9087c478bd9Sstevel@tonic-gate if ($cont_case) { 9097c478bd9Sstevel@tonic-gate err("unexpected ;"); 9107c478bd9Sstevel@tonic-gate } elsif (!$cont_special) { 9117c478bd9Sstevel@tonic-gate err("unexpected ;") if (@cont_paren != 0); 9127c478bd9Sstevel@tonic-gate if (!$cont_first && $cont_multiseg == 1) { 9137c478bd9Sstevel@tonic-gate err_prefix($cont_start, 9147c478bd9Sstevel@tonic-gate "multiple statements continued ". 9157c478bd9Sstevel@tonic-gate "over multiple lines"); 9167c478bd9Sstevel@tonic-gate $cont_multiseg = 2; 9177c478bd9Sstevel@tonic-gate } elsif ($cont_multiseg == 0) { 9187c478bd9Sstevel@tonic-gate $cont_multiseg = 1; 9197c478bd9Sstevel@tonic-gate } 9207c478bd9Sstevel@tonic-gate if ($rest =~ /^$/) { 9217c478bd9Sstevel@tonic-gate $cont_in = 0; 9227c478bd9Sstevel@tonic-gate last; 9237c478bd9Sstevel@tonic-gate } 9247c478bd9Sstevel@tonic-gate if ($rest =~ /^\s*special/) { 9257c478bd9Sstevel@tonic-gate err("if/for/while/switch not started ". 9267c478bd9Sstevel@tonic-gate "on its own line"); 9277c478bd9Sstevel@tonic-gate } 9287c478bd9Sstevel@tonic-gate goto section_ended; 9297c478bd9Sstevel@tonic-gate } 9307c478bd9Sstevel@tonic-gate } elsif (/\{/) { 9317c478bd9Sstevel@tonic-gate err("{ while in parens/brackets") if (@cont_paren != 0); 9327c478bd9Sstevel@tonic-gate err("stuff after {") if ($rest =~ /[^\s}]/); 9337c478bd9Sstevel@tonic-gate $cont_in = 0; 9347c478bd9Sstevel@tonic-gate last; 9357c478bd9Sstevel@tonic-gate } elsif (/\}/) { 9367c478bd9Sstevel@tonic-gate err("} while in parens/brackets") if (@cont_paren != 0); 9377c478bd9Sstevel@tonic-gate if (!$cont_special && $rest !~ /^\s*(while|else)\b/) { 9387c478bd9Sstevel@tonic-gate if ($rest =~ /^$/) { 9397c478bd9Sstevel@tonic-gate err("unexpected }"); 9407c478bd9Sstevel@tonic-gate } else { 9417c478bd9Sstevel@tonic-gate err("stuff after }"); 9427c478bd9Sstevel@tonic-gate } 9437c478bd9Sstevel@tonic-gate $cont_in = 0; 9447c478bd9Sstevel@tonic-gate last; 9457c478bd9Sstevel@tonic-gate } 9467c478bd9Sstevel@tonic-gate } elsif (/\:/ && $cont_case && @cont_paren == 0) { 9477c478bd9Sstevel@tonic-gate err("stuff after multi-line case") if ($rest !~ /$^/); 9487c478bd9Sstevel@tonic-gate $cont_in = 0; 9497c478bd9Sstevel@tonic-gate last; 9507c478bd9Sstevel@tonic-gate } 9517c478bd9Sstevel@tonic-gate next; 9527c478bd9Sstevel@tonic-gatesection_ended: 9537c478bd9Sstevel@tonic-gate # End of a statement or if/while/for loop. Reset 9547c478bd9Sstevel@tonic-gate # cont_special and cont_macro based on the rest of the 9557c478bd9Sstevel@tonic-gate # line. 9567c478bd9Sstevel@tonic-gate $cont_special = ($rest =~ /^\s*$special/)? 1 : 0; 9577c478bd9Sstevel@tonic-gate $cont_macro = ($rest =~ /^\s*$macro/)? 1 : 0; 9587c478bd9Sstevel@tonic-gate $cont_case = 0; 9597c478bd9Sstevel@tonic-gate next; 9607c478bd9Sstevel@tonic-gate } 9617c478bd9Sstevel@tonic-gate $cont_noerr = 0 if (!$cont_in); 9627c478bd9Sstevel@tonic-gate} 963