1#!/usr/bin/env python
2#
3# Copyright 2009, Alexandre Deckner, alex@zappotek.com
4# Distributed under the terms of the MIT License.
5#
6import re, sys, os
7from utils import *
8
9
10def processMatches(matches, name, text, highlights):
11    for match in matches:
12        printMatch(name, match, text)
13        highlights.append((match.start(), match.end(), name))
14
15
16def run(fileSet, rules, outputFileName):
17    openHtml(fileSet, outputFileName)
18
19    for fileName in fileSet:
20        print("\nChecking " + fileName + ":")
21        file = open(fileName, 'r')
22        text = file.read()
23
24        highlights = []
25
26        for name, regexp in rules.items():
27            processMatches(regexp.finditer(text), name, text, highlights)
28
29        highlights.sort()
30        highlights = checkHighlights(highlights)
31
32        file.close()
33
34        renderHtml(text, highlights, fileName, outputFileName)
35
36    closeHtml(outputFileName)
37
38
39def findCppFiles(dir):
40    extensions = [".cpp", ".h"]
41    vcsCacheDirectory = [".bzr", ".git", ".hg", ".svn"]
42
43    results = []
44
45    for root, dirs, files in os.walk(dir):
46        if os.path.split(root)[1] in vcsCacheDirectory:
47            print(root + " cache directory has been ignored")
48            continue
49
50        for file in files:
51            path = os.path.join(root, file)
52            if os.path.splitext(file)[1] in extensions:
53                print("adding", path)
54                results.append(path)
55
56    return results
57
58
59cppRules = {}
60cppRules["Line over 100 char"] = re.compile('[^\n]{101,}')
61cppRules["Spaces instead of tabs"] = re.compile('   ')
62cppRules["Missing space after control statement"] \
63    = re.compile('(for|if|while|switch)\(')
64cppRules["Missing space at comment start"] = re.compile('//\w')
65cppRules["Missing space after operator"] \
66    = re.compile('\w(==|[,=>/+\-*;\|])\w')
67cppRules["Operator at line end"] = re.compile('([*=/+\-\|\&\?]|\&&|\|\|)(?=\n)')
68cppRules["Missing space"] = re.compile('\){')
69cppRules["Mixed tabs/spaces"] = re.compile('( \t]|\t )+')
70cppRules["Malformed else"] = re.compile('}[ \t]*\n[ \t]*else')
71cppRules["Lines between functions > 2"] \
72    = re.compile('(?<=\n})([ \t]*\n){3,}(?=\n)')
73cppRules["Lines between functions &lt; 2"] \
74    = re.compile('(?<=\n})([ \t]*\n){0,2}(?=.)')
75cppRules["Windows Line Ending"] = re.compile('\r')
76cppRules["Bad pointer/reference style"] \
77    = re.compile('(?<=\w) [*&](?=(\w|[,\)]))')
78
79# TODO: ignore some rules in comments
80#cppRules["-Comment 1"] = re.compile('[^/]/\*(.|[\r\n])*?\*/')
81#cppRules["-Comment 2"] = re.compile('(//)[^\n]*')
82
83
84if len(sys.argv) >= 2 and sys.argv[1] != "--help":
85    files = []
86    for arg in sys.argv[1:]:
87        if os.path.isfile(arg):
88            files.append(arg)
89        else:
90            files.extend(findCppFiles(arg))
91
92    run(files, cppRules, "styleviolations.html")
93else:
94    print("Usage: python checkstyle.py file.cpp [file2.cpp] [directory]\n")
95    print("Checks c++ source files against the Haiku Coding Guidelines.")
96    print("Outputs an html report in the styleviolations.html file.\n")
97