1*cdf0c1d5Smjnelson#! /usr/bin/python 2*cdf0c1d5Smjnelson# 3*cdf0c1d5Smjnelson# CDDL HEADER START 4*cdf0c1d5Smjnelson# 5*cdf0c1d5Smjnelson# The contents of this file are subject to the terms of the 6*cdf0c1d5Smjnelson# Common Development and Distribution License (the "License"). 7*cdf0c1d5Smjnelson# You may not use this file except in compliance with the License. 8*cdf0c1d5Smjnelson# 9*cdf0c1d5Smjnelson# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*cdf0c1d5Smjnelson# or http://www.opensolaris.org/os/licensing. 11*cdf0c1d5Smjnelson# See the License for the specific language governing permissions 12*cdf0c1d5Smjnelson# and limitations under the License. 13*cdf0c1d5Smjnelson# 14*cdf0c1d5Smjnelson# When distributing Covered Code, include this CDDL HEADER in each 15*cdf0c1d5Smjnelson# file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*cdf0c1d5Smjnelson# If applicable, add the following below this CDDL HEADER, with the 17*cdf0c1d5Smjnelson# fields enclosed by brackets "[]" replaced with your own identifying 18*cdf0c1d5Smjnelson# information: Portions Copyright [yyyy] [name of copyright owner] 19*cdf0c1d5Smjnelson# 20*cdf0c1d5Smjnelson# CDDL HEADER END 21*cdf0c1d5Smjnelson# 22*cdf0c1d5Smjnelson 23*cdf0c1d5Smjnelson# 24*cdf0c1d5Smjnelson# Copyright 2008 Sun Microsystems, Inc. All rights reserved. 25*cdf0c1d5Smjnelson# Use is subject to license terms. 26*cdf0c1d5Smjnelson# 27*cdf0c1d5Smjnelson# ident "%Z%%M% %I% %E% SMI" 28*cdf0c1d5Smjnelson# 29*cdf0c1d5Smjnelson 30*cdf0c1d5Smjnelson# 31*cdf0c1d5Smjnelson# Check that header files conform to our standards 32*cdf0c1d5Smjnelson# 33*cdf0c1d5Smjnelson# Standards for all header files (lenient): 34*cdf0c1d5Smjnelson# 35*cdf0c1d5Smjnelson# 1) Begin with a comment containing a copyright message 36*cdf0c1d5Smjnelson# 37*cdf0c1d5Smjnelson# 2) Enclosed in a guard of the form: 38*cdf0c1d5Smjnelson# 39*cdf0c1d5Smjnelson# #ifndef GUARD 40*cdf0c1d5Smjnelson# #define GUARD 41*cdf0c1d5Smjnelson# #endif /* [!]GUARD */ 42*cdf0c1d5Smjnelson# 43*cdf0c1d5Smjnelson# The preferred form is without the bang character, but either is 44*cdf0c1d5Smjnelson# acceptable. 45*cdf0c1d5Smjnelson# 46*cdf0c1d5Smjnelson# 3) Has a valid ident declaration 47*cdf0c1d5Smjnelson# 48*cdf0c1d5Smjnelson# Additional standards for system header files: 49*cdf0c1d5Smjnelson# 50*cdf0c1d5Smjnelson# 1) The file guard must take the form '_FILENAME_H[_]', where FILENAME 51*cdf0c1d5Smjnelson# matches the basename of the file. If it is installed in a 52*cdf0c1d5Smjnelson# subdirectory, it must be of the form _DIR_FILENAME_H. The form 53*cdf0c1d5Smjnelson# without the trailing underscore is preferred. 54*cdf0c1d5Smjnelson# 55*cdf0c1d5Smjnelson# 2) All #include directives must use the <> form. 56*cdf0c1d5Smjnelson# 57*cdf0c1d5Smjnelson# 3) If the header file contains anything besides comments and 58*cdf0c1d5Smjnelson# preprocessor directives, then it must be enclosed in a C++ guard of 59*cdf0c1d5Smjnelson# the form: 60*cdf0c1d5Smjnelson# 61*cdf0c1d5Smjnelson# #ifdef __cplusplus 62*cdf0c1d5Smjnelson# extern "C" { 63*cdf0c1d5Smjnelson# #endif 64*cdf0c1d5Smjnelson# 65*cdf0c1d5Smjnelson# #ifdef __cplusplus 66*cdf0c1d5Smjnelson# } 67*cdf0c1d5Smjnelson# #endif 68*cdf0c1d5Smjnelson# 69*cdf0c1d5Smjnelson 70*cdf0c1d5Smjnelsonimport re, os, sys 71*cdf0c1d5Smjnelson 72*cdf0c1d5Smjnelsonclass HeaderFile(object): 73*cdf0c1d5Smjnelson def __init__(self, fh, filename=None, lenient=False): 74*cdf0c1d5Smjnelson self.file = fh 75*cdf0c1d5Smjnelson self.lenient = lenient 76*cdf0c1d5Smjnelson self.lineno = 0 77*cdf0c1d5Smjnelson self.has_copyright = False 78*cdf0c1d5Smjnelson self.eof = False 79*cdf0c1d5Smjnelson 80*cdf0c1d5Smjnelson if filename: 81*cdf0c1d5Smjnelson self.filename = filename 82*cdf0c1d5Smjnelson else: 83*cdf0c1d5Smjnelson self.filename = fh.name 84*cdf0c1d5Smjnelson 85*cdf0c1d5Smjnelson def getline(self): 86*cdf0c1d5Smjnelson for line in self.file: 87*cdf0c1d5Smjnelson self.lineno += 1 88*cdf0c1d5Smjnelson if not line or line.isspace(): 89*cdf0c1d5Smjnelson continue 90*cdf0c1d5Smjnelson else: 91*cdf0c1d5Smjnelson line = line.rstrip('\r\n') 92*cdf0c1d5Smjnelson 93*cdf0c1d5Smjnelson # Recursively join continuation lines 94*cdf0c1d5Smjnelson if line.endswith('\\'): 95*cdf0c1d5Smjnelson line = line[0:-1] + self.getline() 96*cdf0c1d5Smjnelson 97*cdf0c1d5Smjnelson return line 98*cdf0c1d5Smjnelson else: 99*cdf0c1d5Smjnelson self.eof = True 100*cdf0c1d5Smjnelson return '' 101*cdf0c1d5Smjnelson 102*cdf0c1d5Smjnelson # 103*cdf0c1d5Smjnelson # Optionally take a line to start skipping/processing with 104*cdf0c1d5Smjnelson # 105*cdf0c1d5Smjnelson def skipcomments(self, curline=None): 106*cdf0c1d5Smjnelson line = curline or self.getline() 107*cdf0c1d5Smjnelson while line: 108*cdf0c1d5Smjnelson # When lenient, allow C++ comments 109*cdf0c1d5Smjnelson if self.lenient and re.search(r'^\s*//', line): 110*cdf0c1d5Smjnelson line = self.getline() 111*cdf0c1d5Smjnelson continue 112*cdf0c1d5Smjnelson 113*cdf0c1d5Smjnelson if not re.search(r'^\s*/\*', line): 114*cdf0c1d5Smjnelson return line 115*cdf0c1d5Smjnelson 116*cdf0c1d5Smjnelson while not re.search(r'\*/', line): 117*cdf0c1d5Smjnelson # 118*cdf0c1d5Smjnelson # We explicitly exclude the form used in the 119*cdf0c1d5Smjnelson # CDDL header rather than attempting to craft 120*cdf0c1d5Smjnelson # a match for every possibly valid copyright 121*cdf0c1d5Smjnelson # notice 122*cdf0c1d5Smjnelson # 123*cdf0c1d5Smjnelson if re.search(r'Copyright (?!\[yyyy\])', line): 124*cdf0c1d5Smjnelson self.has_copyright = True 125*cdf0c1d5Smjnelson line = self.getline() 126*cdf0c1d5Smjnelson 127*cdf0c1d5Smjnelson if re.search(r'Copyright (?!\[yyyy\])', line): 128*cdf0c1d5Smjnelson self.has_copyright = True 129*cdf0c1d5Smjnelson line = self.getline() 130*cdf0c1d5Smjnelson 131*cdf0c1d5Smjnelson return line 132*cdf0c1d5Smjnelson 133*cdf0c1d5Smjnelson 134*cdf0c1d5Smjnelsondef err(stream, msg, hdr): 135*cdf0c1d5Smjnelson if not hdr.eof: 136*cdf0c1d5Smjnelson stream.write("%s: line %d: %s\n" % 137*cdf0c1d5Smjnelson (hdr.filename, hdr.lineno, msg)) 138*cdf0c1d5Smjnelson else: 139*cdf0c1d5Smjnelson stream.write("%s: %s\n" % (hdr.filename, msg)) 140*cdf0c1d5Smjnelson 141*cdf0c1d5Smjnelson 142*cdf0c1d5Smjnelson# 143*cdf0c1d5Smjnelson# Keyword strings (both expanded and literal) for the various SCMs 144*cdf0c1d5Smjnelson# Be certain to wrap each full expression in parens. 145*cdf0c1d5Smjnelson# 146*cdf0c1d5Smjnelsonidents = [ 147*cdf0c1d5Smjnelson # SCCS 148*cdf0c1d5Smjnelson r'((\%Z\%(\%M\%)\t\%I\%|\%W\%)\t\%E\% SMI)', 149*cdf0c1d5Smjnelson r'(@\(#\)(\w[-\.\w]+\.h)\t\d+\.\d+(\.\d+\.\d+)?\t\d\d/\d\d/\d\d SMI)', 150*cdf0c1d5Smjnelson] 151*cdf0c1d5Smjnelson 152*cdf0c1d5SmjnelsonIDENT = re.compile(r'(%s)' % '|'.join(idents)) 153*cdf0c1d5Smjnelson 154*cdf0c1d5Smjnelson 155*cdf0c1d5Smjnelsondef hdrchk(fh, filename=None, lenient=False, output=sys.stderr): 156*cdf0c1d5Smjnelson found_ident = False 157*cdf0c1d5Smjnelson guard = None 158*cdf0c1d5Smjnelson ret = 0 159*cdf0c1d5Smjnelson 160*cdf0c1d5Smjnelson hdr = HeaderFile(fh, filename=filename, lenient=lenient) 161*cdf0c1d5Smjnelson 162*cdf0c1d5Smjnelson # 163*cdf0c1d5Smjnelson # Step 1: 164*cdf0c1d5Smjnelson # 165*cdf0c1d5Smjnelson # Headers must begin with a comment containing a copyright notice. We 166*cdf0c1d5Smjnelson # don't validate the contents of the copyright, only that it exists 167*cdf0c1d5Smjnelson # 168*cdf0c1d5Smjnelson line = hdr.skipcomments() 169*cdf0c1d5Smjnelson 170*cdf0c1d5Smjnelson if not hdr.has_copyright: 171*cdf0c1d5Smjnelson err(output, "Missing copyright in opening comment", hdr) 172*cdf0c1d5Smjnelson ret = 1 173*cdf0c1d5Smjnelson 174*cdf0c1d5Smjnelson # 175*cdf0c1d5Smjnelson # Step 2: 176*cdf0c1d5Smjnelson # 177*cdf0c1d5Smjnelson # For application header files only, allow the ident string to appear 178*cdf0c1d5Smjnelson # before the header guard. 179*cdf0c1d5Smjnelson if lenient and line.startswith("#pragma ident") and IDENT.search(line): 180*cdf0c1d5Smjnelson found_ident = 1 181*cdf0c1d5Smjnelson line = hdr.skipcomments() 182*cdf0c1d5Smjnelson 183*cdf0c1d5Smjnelson # 184*cdf0c1d5Smjnelson # Step 3: Header guards 185*cdf0c1d5Smjnelson # 186*cdf0c1d5Smjnelson match = re.search(r'^#ifndef\s([a-zA-Z0-9_]+)$', line) 187*cdf0c1d5Smjnelson if not match: 188*cdf0c1d5Smjnelson err(output, "Invalid or missing header guard", hdr) 189*cdf0c1d5Smjnelson ret = 1 190*cdf0c1d5Smjnelson else: 191*cdf0c1d5Smjnelson guard = match.group(1) 192*cdf0c1d5Smjnelson 193*cdf0c1d5Smjnelson if not lenient: 194*cdf0c1d5Smjnelson guardname = os.path.basename(hdr.filename) 195*cdf0c1d5Smjnelson 196*cdf0c1d5Smjnelson # 197*cdf0c1d5Smjnelson # If we aren't being lenient, validate the name of the 198*cdf0c1d5Smjnelson # guard 199*cdf0c1d5Smjnelson # 200*cdf0c1d5Smjnelson 201*cdf0c1d5Smjnelson guardname = guardname.upper() 202*cdf0c1d5Smjnelson guardname = guardname.replace('.', '_').replace('-','_') 203*cdf0c1d5Smjnelson guardname = guardname.replace('+', "_PLUS") 204*cdf0c1d5Smjnelson 205*cdf0c1d5Smjnelson if not re.search(r'^_.*%s[_]?$' % guardname, guard): 206*cdf0c1d5Smjnelson err(output, "Header guard does not match " 207*cdf0c1d5Smjnelson "suggested style (_FILEPATH_H_)", hdr) 208*cdf0c1d5Smjnelson ret = 1 209*cdf0c1d5Smjnelson 210*cdf0c1d5Smjnelson line = hdr.getline() 211*cdf0c1d5Smjnelson if not re.search(r'#define\s%s$' % guard, line): 212*cdf0c1d5Smjnelson err(output, "Invalid header guard", hdr) 213*cdf0c1d5Smjnelson ret = 1 214*cdf0c1d5Smjnelson if not line: 215*cdf0c1d5Smjnelson line = hdr.skipcomments() 216*cdf0c1d5Smjnelson else: 217*cdf0c1d5Smjnelson line = hdr.skipcomments() 218*cdf0c1d5Smjnelson 219*cdf0c1d5Smjnelson 220*cdf0c1d5Smjnelson # 221*cdf0c1d5Smjnelson # Step 4: ident string 222*cdf0c1d5Smjnelson # 223*cdf0c1d5Smjnelson # We allow both the keyword and extracted versions 224*cdf0c1d5Smjnelson # 225*cdf0c1d5Smjnelson if (not found_ident and line.startswith("#pragma ident") and 226*cdf0c1d5Smjnelson not IDENT.search(line)): 227*cdf0c1d5Smjnelson err(output, "Invalid #pragma ident", hdr) 228*cdf0c1d5Smjnelson ret = 1 229*cdf0c1d5Smjnelson else: 230*cdf0c1d5Smjnelson line = hdr.skipcomments(line) 231*cdf0c1d5Smjnelson 232*cdf0c1d5Smjnelson # 233*cdf0c1d5Smjnelson # Main processing loop 234*cdf0c1d5Smjnelson # 235*cdf0c1d5Smjnelson in_cplusplus = False 236*cdf0c1d5Smjnelson found_endguard = False 237*cdf0c1d5Smjnelson found_cplusplus = False 238*cdf0c1d5Smjnelson found_code = False 239*cdf0c1d5Smjnelson 240*cdf0c1d5Smjnelson while line: 241*cdf0c1d5Smjnelson if not (line.startswith('#') or line.startswith('using')): 242*cdf0c1d5Smjnelson found_code = True 243*cdf0c1d5Smjnelson line = hdr.getline() 244*cdf0c1d5Smjnelson continue 245*cdf0c1d5Smjnelson 246*cdf0c1d5Smjnelson match = re.search(r'^#include(.*)$', line) 247*cdf0c1d5Smjnelson if match: 248*cdf0c1d5Smjnelson # 249*cdf0c1d5Smjnelson # For system files, make sure #includes are of the form: 250*cdf0c1d5Smjnelson # '#include <file>' 251*cdf0c1d5Smjnelson # 252*cdf0c1d5Smjnelson if not lenient and not re.search(r'\s<.*>', 253*cdf0c1d5Smjnelson match.group(1)): 254*cdf0c1d5Smjnelson err(output, "Bad include", hdr) 255*cdf0c1d5Smjnelson ret = 1 256*cdf0c1d5Smjnelson elif not in_cplusplus and re.search(r'^#ifdef\s__cplusplus$', 257*cdf0c1d5Smjnelson line): 258*cdf0c1d5Smjnelson # 259*cdf0c1d5Smjnelson # Start of C++ header guard. 260*cdf0c1d5Smjnelson # Make sure it is of the form: 261*cdf0c1d5Smjnelson # 262*cdf0c1d5Smjnelson # #ifdef __cplusplus 263*cdf0c1d5Smjnelson # extern "C" { 264*cdf0c1d5Smjnelson # #endif 265*cdf0c1d5Smjnelson # 266*cdf0c1d5Smjnelson line = hdr.getline() 267*cdf0c1d5Smjnelson if line == 'extern "C" {': 268*cdf0c1d5Smjnelson line = hdr.getline() 269*cdf0c1d5Smjnelson if line != '#endif': 270*cdf0c1d5Smjnelson err(output, "Bad __cplusplus clause", 271*cdf0c1d5Smjnelson hdr) 272*cdf0c1d5Smjnelson ret = 1 273*cdf0c1d5Smjnelson else: 274*cdf0c1d5Smjnelson in_cplusplus = True 275*cdf0c1d5Smjnelson found_cplusplus = True 276*cdf0c1d5Smjnelson else: 277*cdf0c1d5Smjnelson continue 278*cdf0c1d5Smjnelson elif in_cplusplus and re.search(r'^#ifdef\s__cplusplus$', line): 279*cdf0c1d5Smjnelson # 280*cdf0c1d5Smjnelson # End of C++ header guard. Make sure it is of the form: 281*cdf0c1d5Smjnelson # 282*cdf0c1d5Smjnelson # #ifdef __cplusplus 283*cdf0c1d5Smjnelson # } 284*cdf0c1d5Smjnelson # #endif 285*cdf0c1d5Smjnelson # 286*cdf0c1d5Smjnelson line = hdr.getline() 287*cdf0c1d5Smjnelson if line == '}': 288*cdf0c1d5Smjnelson line = hdr.getline() 289*cdf0c1d5Smjnelson if line != '#endif': 290*cdf0c1d5Smjnelson err(output, "Bad __cplusplus clause", 291*cdf0c1d5Smjnelson hdr) 292*cdf0c1d5Smjnelson ret = 1 293*cdf0c1d5Smjnelson else: 294*cdf0c1d5Smjnelson in_cplusplus = False 295*cdf0c1d5Smjnelson else: 296*cdf0c1d5Smjnelson continue 297*cdf0c1d5Smjnelson elif re.search(r'^#endif\s/\* [!]?%s \*/$' % guard, line): 298*cdf0c1d5Smjnelson # 299*cdf0c1d5Smjnelson # Ending header guard 300*cdf0c1d5Smjnelson # 301*cdf0c1d5Smjnelson found_endguard = True 302*cdf0c1d5Smjnelson 303*cdf0c1d5Smjnelson line = hdr.skipcomments() 304*cdf0c1d5Smjnelson 305*cdf0c1d5Smjnelson # 306*cdf0c1d5Smjnelson # Check for missing end clauses 307*cdf0c1d5Smjnelson # 308*cdf0c1d5Smjnelson if (not lenient) and (not found_cplusplus) and found_code: 309*cdf0c1d5Smjnelson err(output, "Missing __cplusplus guard", hdr) 310*cdf0c1d5Smjnelson ret = 1 311*cdf0c1d5Smjnelson 312*cdf0c1d5Smjnelson if in_cplusplus: 313*cdf0c1d5Smjnelson err(output, "Missing closing #ifdef __cplusplus", hdr) 314*cdf0c1d5Smjnelson ret = 1 315*cdf0c1d5Smjnelson 316*cdf0c1d5Smjnelson if not found_endguard: 317*cdf0c1d5Smjnelson err(output, "Missing or invalid ending header guard", hdr) 318*cdf0c1d5Smjnelson ret = 1 319*cdf0c1d5Smjnelson 320*cdf0c1d5Smjnelson return ret 321