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