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