1# -*- coding: utf-8 -*-
2
3import os
4import sys
5from fnmatch import fnmatch
6from optparse import OptionParser
7
8HEADER_PATTERNS				= ["*.hpp", "*.h"]
9INDENTED_INCLUDE_PREFIX		= "#\tinclude "
10IFNDEF_PREFIX				= "#ifndef "
11
12def getIncludeGuardName (headerFile):
13	return '_' + os.path.basename(headerFile).upper().replace('.', '_')
14
15def getRedundantIncludeGuardErrors (fileName):
16	f		= open(fileName, 'rb')
17	errors	= []
18
19	lineNumber = 1
20	prevLine = None
21	for line in f:
22		if line.startswith(INDENTED_INCLUDE_PREFIX):
23			if prevLine is not None and prevLine.startswith(IFNDEF_PREFIX):
24				ifndefName		= prevLine[len(IFNDEF_PREFIX):-1]			# \note -1 to take out the newline.
25				includeName		= line[len(INDENTED_INCLUDE_PREFIX)+1:-2]	# \note +1 to take out the beginning quote, -2 to take out the newline and the ending quote.
26				if getIncludeGuardName(includeName) != ifndefName:
27					errors.append("Invalid redundant include guard around line %d:" % lineNumber)
28					errors.append("guard is %s but included file is %s" % (ifndefName, includeName))
29
30		prevLine = line
31		lineNumber += 1
32
33	f.close()
34	return errors
35
36def isHeader (filename):
37	for pattern in HEADER_PATTERNS:
38		if fnmatch(filename, pattern):
39			return True
40	return False
41
42def getFileList (path):
43	allFiles = []
44	if os.path.isfile(path):
45		if isHeader(path):
46			allFiles.append(path)
47	else:
48		for root, dirs, files in os.walk(path):
49			for file in files:
50				if isHeader(file):
51					allFiles.append(os.path.join(root, file))
52	return allFiles
53
54if __name__ == "__main__":
55	parser = OptionParser()
56	parser.add_option("-q", "--quiet", action="store_true", dest="quiet", default=False, help="only print files with errors")
57
58	(options, args)	= parser.parse_args()
59	quiet			= options.quiet
60	files			= []
61	invalidFiles	= []
62
63	for dir in args:
64		files += getFileList(os.path.normpath(dir))
65
66	print "Checking..."
67	for file in files:
68		if not quiet:
69			print "  %s" % file
70
71		errors = getRedundantIncludeGuardErrors(file)
72		if errors:
73			if quiet:
74				print "  %s" % file
75			for err in errors:
76				print "    %s" % err
77			invalidFiles.append(file)
78
79	print ""
80	if len(invalidFiles) > 0:
81		print "Found %d files with invalid redundant include guards:" % len(invalidFiles)
82
83		for file in invalidFiles:
84			print "  %s" % file
85
86		sys.exit(-1)
87	else:
88		print "All files have valid redundant include guards."
89