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