Added python scripts to check code style
authorTimo Lotterbach <timo.lotterbach@bmw-carit.de>
Thu, 28 Feb 2013 12:49:40 +0000 (04:49 -0800)
committerTimo Lotterbach <timo.lotterbach@bmw-carit.de>
Thu, 14 Mar 2013 12:03:39 +0000 (05:03 -0700)
- check header guards
- check license header in source files
- check only spaces, no tabs
- check line length
- check indentation
- check single statement on same line
- check single definition on line
- check curly braces on separate line
- check consistent whitespace usage
- check for unneccessary empty lines

Each script takes the list of files to check as arguments.
All scripts print a help message if no argument is provided.

Scripts will print all warnings in a gcc-style format, so
default error and warning parsers will detect them.

Signed-off-by: Timo Lotterbach <timo.lotterbach@bmw-carit.de>
18 files changed:
.gitignore
scripts/__init__.py [new file with mode: 0755]
scripts/check_all_styles.py [new file with mode: 0755]
scripts/check_class_name.py [new file with mode: 0755]
scripts/check_consistent_code_style.py [new file with mode: 0755]
scripts/check_curly_braces_alone_on_line.py [new file with mode: 0755]
scripts/check_empty_lines.py [new file with mode: 0755]
scripts/check_header_guards.py [new file with mode: 0755]
scripts/check_indentation.py [new file with mode: 0755]
scripts/check_license.py [new file with mode: 0755]
scripts/check_long_lines.py [new file with mode: 0755]
scripts/check_single_definition_on_line.py [new file with mode: 0755]
scripts/check_single_statement_on_line.py [new file with mode: 0755]
scripts/check_style.sh [new file with mode: 0755]
scripts/check_tabbing_and_spacing.py [new file with mode: 0755]
scripts/common_modules/__init__.py [new file with mode: 0755]
scripts/common_modules/common.py [new file with mode: 0755]
scripts/common_modules/config.py [new file with mode: 0755]

index f0e0e95..e2fe8ec 100644 (file)
@@ -10,3 +10,5 @@ config.h
 config.h.1stpass
 git.kdev4
 .kdev4
+*.pyc
+*.pyo
diff --git a/scripts/__init__.py b/scripts/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/scripts/check_all_styles.py b/scripts/check_all_styles.py
new file mode 100755 (executable)
index 0000000..c8cb972
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+"""
+    Runs all enabled style checking
+"""
+
+import sys, re, string
+from common_modules.common import *
+
+from check_license import check_license_in_file
+from check_header_guards import check_header_guards
+from check_consistent_code_style import check_consistent_code_style
+from check_indentation import check_indentation
+from check_curly_braces_alone_on_line import check_curly_braces_alone_on_line
+from check_long_lines import check_long_lines
+from check_single_definition_on_line import check_single_definition_on_line
+from check_single_statement_on_line import check_single_statement_on_line
+from check_tabbing_and_spacing import check_tabbing_and_spacing
+from check_empty_lines import check_empty_lines
+from check_class_name import check_class_name
+
+#######################################################################################################################
+#CONFIGURATION
+#######################################################################################################################
+CHECK_LICENSE                       = "ON"
+CHECK_HEADER_GUARDS                 = "ON"
+CHECK_TABBING_AND_SPACING           = "ON"
+CHECK_INDENTATION                   = "ON"
+CHECK_CONSISTENT_CODE_STYLE         = "ON"
+CHECK_CURLY_BRACES_ALONE_ON_LINE    = "ON"
+CHECK_LONG_LINES                    = "ON"
+CHECK_SINGLE_STATEMENT_ON_LINE      = "ON"
+CHECK_SINGLE_DEFINITION_ON_LINE     = "ON"
+CHECK_EMPTY_LINES                   = "ON"
+CHECK_CLASS_NAME                    = "ON"
+#######################################################################################################################
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\tTakes a list of files and directories as input and runs all enabled style-checking on all files.
+
+\tTo enable/disable style-checking you can toggle the "ON"/"OFF" value of the configuration variables inside the file.
+"""
+        exit(0)
+    
+    for t in targets:
+        if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2:] == ".c":
+            file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
+            
+            if CHECK_LICENSE == "ON":
+                check_license_in_file(t, file_contents)
+            if CHECK_HEADER_GUARDS == "ON" and t[-2:] == ".h":
+                check_header_guards(t, file_contents)
+            if CHECK_TABBING_AND_SPACING == "ON":
+                check_tabbing_and_spacing(t, file_lines)
+            if CHECK_INDENTATION == "ON":
+                check_indentation(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
+            if CHECK_CONSISTENT_CODE_STYLE == "ON":
+                check_consistent_code_style(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
+            if CHECK_CURLY_BRACES_ALONE_ON_LINE == "ON":
+                check_curly_braces_alone_on_line(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
+            if CHECK_LONG_LINES == "ON":
+                check_long_lines(t, file_lines)
+            if CHECK_SINGLE_STATEMENT_ON_LINE == "ON":
+                check_single_statement_on_line(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
+            if CHECK_SINGLE_DEFINITION_ON_LINE == "ON":
+                check_single_definition_on_line(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
+            if CHECK_EMPTY_LINES == "ON":
+                check_empty_lines(t, file_contents, file_lines)
+            if CHECK_CLASS_NAME == "ON" and t[-2:] == ".h":
+                check_class_name(t, clean_file_contents)
diff --git a/scripts/check_class_name.py b/scripts/check_class_name.py
new file mode 100755 (executable)
index 0000000..ef08ca6
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+
+
+def check_class_name(filename, clean_file_contents):
+    #a class is followed by classname, and possible an inherticance
+    class_def_re = re.compile(r'(?<!\w)class(\s+)(\w+)(\s*)((:( |\w|,)*)?)(\s*)\{')
+    class_name_re = re.compile(r'(?<=((?<!\w)class))(\s+\w+)')
+
+    #filename without path and extension
+    good_name = filename
+
+    #remove path from filename (if needed)
+    if good_name.count("/") > 0:
+        good_name = good_name[good_name.rfind("/") + 1:]
+
+    #remove .h extension
+    good_name = good_name[:good_name.find(".h")]
+
+    class_found = False
+
+    for class_match in re.finditer(class_def_re, clean_file_contents):
+        line_number = clean_file_contents[:class_match.end()].count("\n")
+
+        if class_found:
+            log_warning(filename, line_number, "several class difinitions in file")
+        else:
+            class_found = True
+
+            class_name_match = class_name_re.search(clean_file_contents, class_match.start())
+            class_name = clean_file_contents[class_name_match.start(): class_name_match.end()] if class_name_match != None else ""
+            class_name = class_name.strip(" \n\r\f\t")
+
+            if class_name != good_name:
+                log_warning(filename, line_number, "class name has to be identical to filename")
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
+
+\tGives warnings if a file contains more than one class definition or if class name is not identical to file name
+"""
+        exit(0)
+
+    for t in targets:
+        if t[-2:] == ".h":
+            _, clean_file_contents, _, _= read_file(t)
+            check_class_name(t, clean_file_contents)
diff --git a/scripts/check_consistent_code_style.py b/scripts/check_consistent_code_style.py
new file mode 100755 (executable)
index 0000000..a3aae73
--- /dev/null
@@ -0,0 +1,185 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+
+
+
+def check_binary_operators(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+    #normal binary operators must all have 0 or 1 space between operator and operands
+    binary_ops = ['=', r'\+', '%', r'\^', '==', '!=', '>=', '<=', '&&', r'\|\|', r'\|', '<<', '>>', r'\+=', '-=', r'\*=', '/=', '%=',  '&=', r'\|=', r'\^=', '<<=', '>>=', ':', r'\?']
+    #operators that are not handled
+    #r'\*', '&', '>', '<', '/', '-'
+    #access binary operators must have no space between operator and operands
+    binary_ops_re_text = '(' + "|".join(binary_ops) + ')'
+    
+    #re for NO space between operator and operands
+    nospace_re = re.compile(r"\w{0}\w".format(binary_ops_re_text))
+    #re for ONE space between operator and operands
+    space_re = re.compile(r"\w\s{0}\s\w".format(binary_ops_re_text))
+    #unbalanced is a match for binary op (\w\s*{0}\s*\w) that is neither no space (?!(\w{0}\w)) nor single space (?!(\w\s{0}\s\w))
+    #this works because (?!...) does not consume strings 
+    unbalanced_spaces_re = re.compile(r"(?!(\w{0}\w))(?!(\w\s{0}\s\w))(\w\s*{0}\s*\w)".format(binary_ops_re_text))
+    
+    #access_ops = ['.', '->', '::']
+    access_ops_re_text = r'(\.|->|::)'
+    bad_access_ops_re = re.compile(r"(\w\s+{0}\s*\w)|(\w\s*{0}\s+\w)".format(access_ops_re_text))
+    
+    #check if there is general inconsistance in the file
+    nospace_match = re.search(nospace_re, clean_file_contents)
+    space_match = re.search(space_re, clean_file_contents)
+    
+    if nospace_match != None and space_match != None:
+        line1 = clean_file_contents[:nospace_match.start()].count("\n") + 1
+        line2 = clean_file_contents[:space_match.start()].count("\n") + 1
+        
+        log_warning(filename, line1, "inconsistent spacing between operands and binary operator. All operands in a file must have same space (0 or 1 spaces) [Reported once per file]", "lines :{0} and {1}".format(line1, line2))
+    
+    for i in range(len(file_lines)):
+        line = clean_file_lines[i]
+        
+        if re.search(unbalanced_spaces_re, line) != None:
+            log_warning(filename, i + 1, "incorrect or unbalanced spaces between operands and binary operator", file_lines[i].strip(" "))
+        
+        if re.search(bad_access_ops_re, line) != None:
+            log_warning(filename, i + 1, "unneeded space(s) around access/resolution operator", file_lines[i].strip(" "))
+
+def check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+    #check if there is extra space before semicolon, or missing space after semicolon (if there is content after it like in for loop)
+    space_before_re = re.compile(r"\s;");
+    nospace_after_re = re.compile(r";((?! ).)");
+
+    for i in range(len(file_lines)):
+        line = clean_file_lines[i]
+        
+        if re.search(space_before_re, line) != None:
+            log_warning(filename, i + 1, "unneeded space(s) before semicolon", file_lines[i].strip(" "))
+        
+        if re.search(nospace_after_re, line) != None:
+            log_warning(filename, i + 1, "missing space after semicolon", file_lines[i].strip(" "))
+
+def check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+    #check if there are spaces between between right/left brace and contents
+    bad_left_brace_re = re.compile(r"\(\s")
+    bad_right_brace_re = re.compile(r"\s\)")
+    
+    for i in range(len(file_lines)):
+        line = clean_file_lines[i]
+        
+        if re.search(bad_left_brace_re, line) != None:
+            log_warning(filename, i + 1, "unneeded space(s) after left brace", file_lines[i].strip(" "))
+            
+        if re.search(bad_right_brace_re, line) != None:
+            log_warning(filename, i + 1, "unneeded space(s) before right brace", file_lines[i].strip(" "))
+
+def check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+    nospace_after_re = re.compile(r",((?!\s).)")
+    too_many_spaces_after_re = re.compile(r",\s(\s+)")
+    space_before_re = re.compile(r"\S(\s+),")
+    
+    for i in range(len(file_lines)):
+        line = clean_file_lines[i]
+        
+        if re.search(nospace_after_re, line) != None:
+            log_warning(filename, i + 1, "missing space after comma", file_lines[i].strip(" "))
+            
+        if re.search(too_many_spaces_after_re, line) != None:
+            log_warning(filename, i + 1, "unneeded space(s) after comma", file_lines[i].strip(" "))
+
+        if re.search(space_before_re, line) != None:
+            log_warning(filename, i + 1, "unneeded space(s) before comma", file_lines[i].strip(" "))
+
+def check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+    #in bad function definition or call there exists a space (or more) between function name and left brace (the last \s+ in the expression)
+    #the function name must not be a if, for, switch or while
+    reserved_words = ['if', 'for', 'while', 'switch', 'return', 'delete']
+    reserved_words_re_text = "(?<!\W" + ")(?<!\W".join(reserved_words) + ")"
+
+    #the bad function is anything except the reserved words, and the closing brace is not directly followed by another open brace
+    #(to differentiate function calls/defs from function pointer defs)
+    #the \s+ is the fauled pattern that should be caught, because it means a space exists between function name and left parenthese
+    bad_func_re_text = r"(\w+){0}(\s+)(\()".format(reserved_words_re_text)
+    bad_func_re = re.compile(bad_func_re_text)
+        
+    #function pointer
+    return_type_re_text = r'\w+\s*(&|\*)*\s*'
+    func_ptr_re_text = r'\s*{0}\((\w|\*|:|\s)+\)(\s*)\('.format(return_type_re_text)
+
+    func_ptr_re = re.compile(func_ptr_re_text)
+
+    macro_re = re.compile(r"(\s*)#define")
+    
+    for i in range(len(file_lines)):
+        line = clean_file_lines[i]
+
+        for bad_func_match in bad_func_re.finditer(line):
+            #print line[bad_func_match.start():], re.match(func_ptr_re, line[bad_func_match.start():]) != None
+            if re.search(func_ptr_re, line) == None and re.match(macro_re, line) == None:
+                log_warning(filename, i + 1, "unneeded space(s) between function name and left brace", file_lines[i].strip(" "))
+
+def check_space_before_braces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines):
+    reserved_words = ['if', 'for', 'while', 'switch', 'return']
+    reserved_words_re_text = r'\W(' + "|".join(reserved_words) + ')'
+    
+    nospace_re = re.compile(r"{0}\(".format(reserved_words_re_text))
+    space_re = re.compile(r"{0}\s\(".format(reserved_words_re_text))
+    too_many_space_re = re.compile(r"{0}\s(\s+)\(".format(reserved_words_re_text))
+    
+    nospace_match = re.search(nospace_re, clean_file_contents)
+    space_match = re.search(space_re, clean_file_contents)
+    if nospace_match != None and space_match != None:
+        line1 = clean_file_contents[:nospace_match.start()].count("\n") + 1
+        line2 = clean_file_contents[:space_match.start()].count("\n") + 1
+        
+        log_warning(filename, line1, "inconsistent spacing between keyword and left brace. All braces in a file must have same space (0 or 1 spaces) [Reported once per file]", "lines :{0} and {1}".format(line1, line2))
+    
+    for i in range(len(file_lines)):
+        line = clean_file_lines[i]
+        
+        if re.search(too_many_space_re, line) != None:
+            log_warning(filename, i + 1, "unneeded space(s) between keyword and left brace", file_lines[i].strip(" "))
+
+def check_consistent_code_style(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+    check_function_call_or_definition(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
+    check_semicolor(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
+    check_space_before_braces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
+    check_braces_spaces(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
+    check_commas(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
+    check_binary_operators(filename, file_contents, file_lines, clean_file_contents, clean_file_lines)
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories
+
+\tGives warnings if there is inconsistency in spacing of commas, semicolons, operands, braces, function calls or definitions.
+"""
+        exit(0)
+    
+    for t in targets:
+        if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
+            file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
+            check_consistent_code_style(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
diff --git a/scripts/check_curly_braces_alone_on_line.py b/scripts/check_curly_braces_alone_on_line.py
new file mode 100755 (executable)
index 0000000..3e57ccc
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+
+def remove_tollerable_braces(clean_file_contents):
+    #remove array assignments
+    assignment_re = re.compile(r'=(\s*)(\{)((?!;).)*(\})(\s*);', re.DOTALL)
+    
+    no_assig_contents = clean_file_contents
+    found_match = re.search(assignment_re, no_assig_contents)
+    
+    while found_match != None:
+        matched_string = no_assig_contents[found_match.start(): found_match.end()]
+        no_assig_contents = no_assig_contents[:found_match.start()] + "\n" * matched_string.count("\n") + no_assig_contents[found_match.end():]
+
+        found_match = re.search(assignment_re, no_assig_contents)
+
+    #remove braces that are used for implementing empty functions
+    empty_func_re = re.compile(r'\{\s*\}')
+
+    no_empty_func_contents = no_assig_contents
+    found_match = re.search(empty_func_re, no_empty_func_contents)
+
+    while found_match != None:
+        matched_string = no_empty_func_contents[found_match.start(): found_match.end()]
+        no_empty_func_contents = no_empty_func_contents[:found_match.start()] + "\n" * matched_string.count("\n") + no_empty_func_contents[found_match.end():]
+
+        found_match = re.search(empty_func_re, no_empty_func_contents)
+
+    return no_empty_func_contents
+
+def check_curly_braces_alone_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+    clean_file_contents = remove_tollerable_braces(clean_file_contents)
+    clean_file_lines = clean_file_contents.split('\n')
+    
+    typedecl_stack = []
+    #a type declaration contains the words struct, class , union or enum
+    typedecl_re = re.compile(r"(?!<\w)(typedef)?(\s*)(struct|class|enum|union)(?!\w)")
+    #some statements are allowed to have curly brace on same
+    permissible_re = re.compile(r'''(\s*)
+                                (
+                                    do| # do keyword
+                                    (namespace((?!\{).)*)| # namespace keyword 
+                                    (extern((?!\{).)*) # extern keyword
+                                )?
+                                (\s*)\{(\s*)
+                                (\\?)(\s*)
+                                $''', re.VERBOSE)
+
+    
+    for i in range(len(file_lines)):
+        line = clean_file_lines[i]
+        
+        def get_former_line():
+            if i > 0:
+                for j in range(i - 1, 0 , -1):
+                    if len(clean_file_lines[j].strip(" \t\n\f\r")):
+                        return clean_file_lines[j]
+            return None
+        
+        former_line = get_former_line()
+        
+        if line.count("{"):
+            typedecl = re.search(typedecl_re, former_line)
+            typedecl_stack.append(typedecl)
+            
+            if re.match(permissible_re, line) == None :
+                log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" "))
+        
+        if line.count("}"):
+            typedecl = typedecl_stack.pop()
+            if typedecl:
+                if re.match(r"(\s*)\}", line) == None:
+                    log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" "))
+            else:
+                if re.match(r"(\s*)\}(\s*)((while((?!;).)*)?)(;?)(\s*)(\\?)(\s*)$", line) == None:
+                    log_warning(filename, i + 1, "other code on same line with curly brace", file_lines[i].strip(" "))
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories
+
+\tGives warnings if unnecessary code exists on the same line with curly braces. 
+"""
+        exit(0)
+    
+    for t in targets:
+        if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
+            file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
+            check_curly_braces_alone_on_line(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
diff --git a/scripts/check_empty_lines.py b/scripts/check_empty_lines.py
new file mode 100755 (executable)
index 0000000..e5f51f2
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+from common_modules.config import G_MAX_INDENTATION_THRESHOLD
+
+
+def check_empty_lines(filename, file_contents, file_lines):
+    #keep track of the current indentation
+    #need only to know if level of indent is zero or not
+    line_indent_zero = True
+
+    def is_line_empty(l):
+        return len(l.strip(" \t\n\r\f")) == 0
+
+    #check all lines except last line
+    for i in range(len(file_lines) - 1):
+        line = file_lines[i]
+        former_line = file_lines[i - 1] if i > 0 else ""
+        next_line = file_lines[i + 1] if i < len(file_lines) - 1 else ""
+
+        def is_next_indent_zero():
+            for j in range(i + 1, len(file_lines)):
+                if not is_line_empty(file_lines[j]):
+                    return re.match(r'(\s+)(\S+)', file_lines[j]) == None
+            return True
+
+        next_indent_zero = is_next_indent_zero()
+
+        #if empty line
+        if is_line_empty(line):
+            #check if line is not needed
+            #if the line before it was empty (two consecutive white space lines)
+            if is_line_empty(former_line) and not line_indent_zero and not next_indent_zero:
+                log_warning(filename, i + 1, "unneeded empty line (two or more consecutive empty lines)")
+            #if the line before it was a block beginning
+            elif re.search(r'\{(\s*)$', former_line):
+                log_warning(filename, i + 1, "unneeded empty line (block begins with empty line)")
+            #if the line after it is a block end
+            elif re.match(r'(\s*)\}', next_line):
+                log_warning(filename, i + 1, "unneeded empty line (block ends with empty line)")
+        #if non-empty line
+        else:
+            #if the line contains one or more white-space characters followed by
+            #one or more non white-space characters
+            line_indent_zero = re.match(r'(\s+)(\S+)', line) == None
+
+    if re.search(r'\n$', file_contents) == None:
+        log_warning(filename, len(file_lines), "file does not end with new line")
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories
+
+\tGives warnings if there are unneeded empty lines
+\tIt also gives a warning if there is NO empty line at end of file (because this leads to warnings on some compilers)
+"""
+        exit(0)
+
+    for t in targets:
+        if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
+            file_contents, _, file_lines, _ = read_file(t)
+            check_empty_lines(t, file_contents, file_lines)
diff --git a/scripts/check_header_guards.py b/scripts/check_header_guards.py
new file mode 100755 (executable)
index 0000000..0dbb7d5
--- /dev/null
@@ -0,0 +1,117 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+
+def check_header_guards(filename, file_contents):
+    #remove strings
+    string_re = re.compile(r'"((?!((?<!((?<!\\)\\))")).)*"', re.DOTALL)
+    file_contents = clean_string_from_regex(file_contents, string_re, 'x')
+
+    #remove multi-line comments
+    ml_comment_re = re.compile(r'(\s*)/\*((?!\*/).)*\*/', re.DOTALL)
+    file_contents = clean_string_from_regex(file_contents, ml_comment_re, '')
+    
+    #remove single line comments
+    sl_comment_re = re.compile("//.*$", re.MULTILINE)
+    file_contents = re.sub(sl_comment_re, '', file_contents)
+    
+    file_contents = file_contents.strip(" \t\n\f\r")
+
+    
+    def check_good_name(name):
+        #good name
+        good_name = filename
+        
+        #remove path from filename (if needed)
+        if good_name.count("/") > 0:
+            good_name = good_name[good_name.rfind("/") + 1:]
+        
+        #remove .h extension
+        good_name = good_name[:good_name.find(".h")]
+        
+        #good name is all uppercase
+        good_name = good_name.upper()
+        
+        #a good name can start with underscores, and can end with underscores, can optionally have a capital H character
+        good_name_re = re.compile("(_*){0}(_+H?_*)?".format(good_name))
+        
+        return re.match(good_name_re, name) != None
+    
+    #find the ifndef guard
+    ifndef_re = re.compile(r"(\s*)#ifndef .*", re.MULTILINE)
+    ifndef_match = re.match(ifndef_re, file_contents)
+    if ifndef_match == None:
+        log_warning(filename, 1, "header file does not contain a valid #ifndef guard at the beginning")
+        return None
+    
+    ifndef_match_text = file_contents[:ifndef_match.end()]
+    ifndef_match_text = ifndef_match_text.strip(" \n\r\f\t")
+    splitted_match_text = ifndef_match_text.split(" ")
+    ifndef_guard_name = splitted_match_text[1] if len(splitted_match_text) > 1 else ""
+    
+    if not check_good_name(ifndef_guard_name):
+        log_warning(filename, 1, "#ifndef {0} guard name does not follow convention, name has to contain file name in uppercase".format(ifndef_guard_name))
+        return None
+    
+    file_contents = file_contents[ifndef_match.end():]
+    
+    #find the define guard
+    define_re = re.compile(r"(\s*)#define .*")
+    define_match = re.match(define_re, file_contents)
+    if define_match == None:
+        log_warning(filename, 1, "header file does not contain a #define guard at the beginning that matches #ifndef {0}".format(ifndef_guard_name))
+        return None
+    
+    define_match_text = file_contents[:define_match.end()]
+    define_match_text = define_match_text.strip(" \n\r\f\t")
+    splitted_match_text = define_match_text.split(" ") 
+    define_guard_name = splitted_match_text[1] if len(splitted_match_text) > 1 else ""
+    
+    if define_guard_name != ifndef_guard_name:
+        log_warning(filename, 1, "header guard names do not match :(#ifndef {0}) and (#define {1})".format(ifndef_guard_name, define_guard_name))
+    
+    #find the endif guard
+    endif_re = re.compile(r"#endif.*(\s*)$")
+    endif_match = re.search(endif_re, file_contents)
+    if endif_match == None:
+        log_warning(filename, 1, "header file does not end with #endif at last line")
+        return None
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories
+
+\tGives warnings if header guards does not exist in header files. Header files must have a #ifndef and #define guards directly after the license.
+\tThe names of the header guards must match together and match with the name of the file in uppercase letters.
+"""
+        exit(0)
+    
+    for t in targets:
+        if t[-2:] == ".h":
+            file_contents, _, _, _ = read_file(t)
+            check_header_guards(t, file_contents)
diff --git a/scripts/check_indentation.py b/scripts/check_indentation.py
new file mode 100755 (executable)
index 0000000..4ae6c83
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+from common_modules.config import G_MAX_INDENTATION_THRESHOLD
+
+def check_indentation(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+    #It is important to keep track of the changes made to the indent level because
+    #curly braces of namespaces do not affect indentation, while curly braces of
+    #other statements like if, for and while increase/decease indentation
+    indent_levels_stack = [0]
+    indent_level = 0
+    parentheses_count = 0
+    
+    def is_incomplete_statement(line, former_line):
+        incomplete_statement = False
+        #include statements not preceded by a semicolon in previous line
+        incomplete_statement |= former_line != None and re.search(re.compile(r";(\s*)$"), former_line) == None
+        #exclude lines containing block beginnings and ends
+        incomplete_statement &= re.search(re.compile(r"(\{|\})(\s*)$"), line) == None
+        #exclude statements preceded by block beginnings and ends
+        incomplete_statement &= former_line != None and re.search(re.compile(r"(\{|\})(\s*)$"), former_line) == None
+        #exclude preprocessor directives (they dunt have to end with a ; for example)
+        incomplete_statement &= re.match(re.compile(r"(\s*)#"), line) == None
+        #exclude statements preceded by preprocessor directives that do not end with a slash \
+        incomplete_statement &= former_line != None and re.match(re.compile(r"(\s*)#((?!\/).)*\/$"), former_line) == None
+
+
+        #filter out the case where a line is preceeded by a label statement
+        fl_label_statement = False
+        if former_line != None:
+            #case statements -> case id : expr that does not begin with a : (to avoid matching with :: operator)
+            fl_label_statement = re.match(r'(\s*)case(\s*)((?!:).)*(\s*):(?!:)(\s*)$', former_line) != None
+            #label statements -> label : expr that does not begin with a : (to avoid matching with :: operator)
+            fl_label_statement |= re.match(r'(\s*)(\w+)(\s*):(?!:)(\s*)$', former_line) != None
+
+        incomplete_statement &= not fl_label_statement
+
+        #include statements that are contained inside parentheses
+        incomplete_statement |= parentheses_count > 0
+        
+        return incomplete_statement
+    
+    def is_label_statement(line):
+        label_statement = False
+        if line != None:
+            #case statements -> case id : expr that does not begin with a : (to avoid matching with :: operator)
+            label_statement = re.match(r'(\s*)case(\s*)((?!:).)*(\s*):(?!:)', line) != None
+            #label statements -> label : expr that does not begin with a : (to avoid matching with :: operator)
+            label_statement |= re.match(r'(\s*)(\w+)(\s*):(?!:)', line) != None
+        return label_statement
+    
+    def is_preprocessor_directive(line):
+        return re.match(r"(\s*)#", line) != None
+
+    def is_macro_body(former_line):
+        return former_line != None and re.search(r"\\$", former_line) != None
+    
+    for i in range(len(clean_file_lines)):
+        line = clean_file_lines[i]
+        line_indent = 0
+        
+        #a loop is to account for cases where there are several braces on same line
+        for _ in range(0, line.count("}") - line.count("{")):
+            indent_level = indent_levels_stack.pop()
+        
+        #check correct line indentation
+        found_match = re.search("(?! )", line)
+        if found_match:
+            line_indent = found_match.start() / 4.0
+        
+        def get_former_line():
+            if i > 0:
+                for j in range(i - 1, 0 , -1):
+                    if len(clean_file_lines[j].strip(" \t\n\f\r")):
+                        return clean_file_lines[j]
+            return ""
+        
+        former_line = get_former_line()
+        
+        incomplete_statement = is_incomplete_statement(line, former_line)
+        label_statement = is_label_statement(line)
+        fl_label_statement = is_label_statement(former_line)
+        preprocessor_directive = is_preprocessor_directive(line)
+        macro_body = is_macro_body(former_line)
+        
+        if len(line.strip(" \t\n\r")) > 0:
+            if macro_body:
+                #do not check anything, just ignore line
+                None
+            elif preprocessor_directive:
+                #warning if not at 0 level indentation
+                if line_indent != 0:
+                    log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
+            elif label_statement:
+                #warning if label statement has one level less indentation than the normal indentation level
+                if line_indent != indent_level - 1:
+                    log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
+            elif (not incomplete_statement) and line_indent != indent_level:
+                #warning if a complete statement (NON incomplete statement) is not at the normal level of indentation 
+                log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
+            elif incomplete_statement:
+                if fl_label_statement and line_indent != indent_level:
+                    #warning if preceeded by a label statement but not exactly at normal indentation
+                    log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
+                elif (not fl_label_statement ) and line_indent < indent_level:
+                    #warning if incomplete statement and indentation is less than normal indentation
+                    log_warning(filename, i + 1, "incorrect indentation", file_lines[i].strip(" "))
+        
+        #a loop is to account for cases where there are several braces on same line
+        for _ in range(0, line.count("{") - line.count("}")):
+            indent_levels_stack.append(indent_level)
+            #if there is no namesapce or extern declaration before curly brace: increase indentation
+            #if re.match(r"(\s*)((namespace(\s+\w+)?(\s*)\{)|(extern(((?!\{).)*)\{))", line) == None and re.match(r"(\s*)(namespace|extern)", former_line) == None:
+            #if re.match(r"(\s*)(namespace)", line) == None and re.match(r"(\s*)namespace", former_line) == None:
+            namespace_or_extern_re_text = r'\s*(namespace|(extern((\s*\S*\s*)?)(\s*)($|\{)))'
+            namespace_or_extern_re = re.compile(namespace_or_extern_re_text)
+
+            if re.match(namespace_or_extern_re, line) == None and re.match(namespace_or_extern_re, former_line) == None:
+                indent_level += 1
+                
+                #warning if indentation level is too much
+                if indent_level == G_MAX_INDENTATION_THRESHOLD + 1:
+                    log_warning(filename, i + 1, "code block has too much indentation [maximum level of indentation is {0}]".format(G_MAX_INDENTATION_THRESHOLD),)
+        
+        parentheses_count += line.count("(") - line.count(")")
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories
+
+\tGives warnings if lines (except multi-line comments and macros) do not have correct indentation.
+\tIt also checks if code blocks have too much indentation (too deep). Code should have a maximum of {0} indentation levels.
+""".format(G_MAX_INDENTATION_THRESHOLD)
+        exit(0)
+    
+    for t in targets:
+        if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
+            file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
+            check_indentation(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
diff --git a/scripts/check_license.py b/scripts/check_license.py
new file mode 100755 (executable)
index 0000000..340c31b
--- /dev/null
@@ -0,0 +1,128 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+
+BMW_LICENSE_TEMPLATE = '''/***************************************************************************
+*
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*        http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+****************************************************************************/'''
+
+
+def clean_comment_chars(s):
+    """
+        Removes comment characters from a string
+    """
+    s = string.replace(s, "/", "")
+    s = string.replace(s, "*", "")
+    s = string.replace(s, "#", "")
+    return s
+
+def make_license_re(license_text):
+    """
+        Makes a regular expression for every line in the license, this would match the license
+        text tolerating extra spaces
+    """
+    license_lines = license_text.split("\n")
+    license_lines_re = {}
+    for i in range(len(license_lines)):
+        license_line = license_lines[i]
+        re_text = clean_comment_chars(license_line)
+        re_text = re_text.strip(" \n\t\r\f")
+        re_text = string.replace(re_text, "(", "\(")
+        re_text = string.replace(re_text, ")", "\)")
+        re_text = string.replace(re_text, " ", "(\s+)")
+        re_text = string.replace(re_text, "\n", "(\s*)")
+        re_text = string.replace(re_text, "\t", "(\s+)")
+        re_text = string.replace(re_text, ".", "\.")
+        
+        re_text = string.replace(re_text, "[YYYY]", r"(\d{4})(((-(\d{4}))|(((\s*),(\s*)\d{4})+))?)")
+        
+        if len(re_text) > 0:
+            re_text = "(\s*)" + re_text + "(\s*)"
+            current_text = ""
+            while current_text != re_text:
+                current_text = re_text
+                re_text = string.replace(re_text, "(\s*)(\s*)", "(\s*)")
+                re_text = string.replace(re_text, "(\s+)(\s+)", "(\s+)")
+                re_text = string.replace(re_text, "(\s*)(\s+)", "(\s+)")
+
+            
+            license_lines_re[i] = re_text
+
+    return license_lines_re
+
+
+def check_license_in_file(filename, file_contents):
+    clean_file_contents = clean_comment_chars(file_contents)
+    license_text = BMW_LICENSE_TEMPLATE
+    
+    license_lines = license_text.split("\n")
+    
+    license_re = make_license_re(license_text)
+    
+    #search for the first line of the license in the target file
+    line_re = re.compile(license_re.values()[0])
+    found_match = line_re.search(clean_file_contents)
+    
+    if found_match:
+        clean_file_contents = clean_file_contents[found_match.start():]
+        
+    #check license as it is
+    for (line_num, line_re_text) in license_re.items():
+        line_re = re.compile(line_re_text)
+        found_match = line_re.match(clean_file_contents)
+        
+        if found_match:
+            clean_file_contents = clean_file_contents[found_match.end():]
+        else:
+            log_warning(filename, 1, "license does not match at", license_lines[line_num])
+            return None
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
+
+\tGives warnings if the file does not contain a valid license text. It does not check if Copyright statements are included.
+"""
+        exit(0)
+    
+    for t in targets:
+        file_contents, _, _, _ = read_file(t)
+        check_license_in_file(t, file_contents)
diff --git a/scripts/check_long_lines.py b/scripts/check_long_lines.py
new file mode 100755 (executable)
index 0000000..63da64d
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+from common_modules.config import G_MAX_LINE_LENGTH_THRESHOLD
+
+
+def check_long_lines(filename, file_lines):
+    string_re = re.compile(r'"((?!((?<!((?<!\\)\\))")).)*"', re.DOTALL)
+    
+    for i in range(len(file_lines)):
+        line = file_lines[i]
+        
+        #if line is very long and DOES NOT contain strings
+        if len(line) > G_MAX_LINE_LENGTH_THRESHOLD and re.search(string_re, line) == None:
+            log_warning(filename, i + 1, "line too long, {0} characters [maximum is {1}]".format(len(line), G_MAX_LINE_LENGTH_THRESHOLD))
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
+
+\tGives warnings if there are too long lines for readability. A line can have a maximum of {0} characters.
+""".format(G_MAX_LINE_LENGTH_THRESHOLD)
+        exit(0)
+    
+    for t in targets:
+        _, _, file_lines, _ = read_file(t)
+        check_long_lines(t, file_lines)
diff --git a/scripts/check_single_definition_on_line.py b/scripts/check_single_definition_on_line.py
new file mode 100755 (executable)
index 0000000..13cdacf
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+
+def check_single_definition_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+    #variable name can be preceeded by * for pointers or & for references, can be followed by an assignment
+    var_decl_re_text = r"(((\**)|(&?))(\s*)(\w+)((=((?![,{]).)*)?))"
+    #types can be in namespaces or nested classes
+    no_template_type_re_text = r"\w+(::\w+)*"
+    simple_several_defs_re = re.compile(r"^(\s*){0}(\s+){1}((\s*),(\s*){1})+;".format(no_template_type_re_text, var_decl_re_text), re.MULTILINE)
+    
+    for match in re.finditer(simple_several_defs_re, clean_file_contents):
+        line_number = clean_file_contents.count("\n", 0, match.end())
+        log_warning(filename, line_number + 1, "several definitions on same line", file_lines[line_number].strip(" \t\r\n"))
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
+
+\tGives warnings if a line contains several variable definitions (This does not include lines that contain template or array declarations).
+"""
+        exit(0)
+    
+    for t in targets:
+        if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
+            file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
+            check_single_definition_on_line(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
diff --git a/scripts/check_single_statement_on_line.py b/scripts/check_single_statement_on_line.py
new file mode 100755 (executable)
index 0000000..7096e13
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+
+def check_single_statement_on_line(filename, file_contents, clean_file_contents, file_lines, clean_file_lines):
+    open_parantheses_count = 0
+    
+    for i in range(len(file_lines)):
+        line = clean_file_lines[i]
+        
+        open_parantheses_count += line.count("(") - line.count(")")
+        
+        #check if there are several semicolons on the line and that is not in the middle of a for statement
+        if line.count(";") > 1 and open_parantheses_count == 0:
+            #check that there is NO right parentheses after semicolon
+            if line.rfind(";") > line.rfind(")"):
+                log_warning(filename, i + 1, "several statements on same line", file_lines[i].strip(" "))
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+    
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
+
+\tGives warnings if a line contains several statements.
+"""
+        exit(0)
+    
+    for t in targets:
+        if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
+            file_contents, clean_file_contents, file_lines, clean_file_lines = read_file(t)
+            check_single_statement_on_line(t, file_contents, clean_file_contents, file_lines, clean_file_lines)
diff --git a/scripts/check_style.sh b/scripts/check_style.sh
new file mode 100755 (executable)
index 0000000..e3c579e
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+
+
+DIRECTORY=$(cd `dirname $0` && pwd)
+cd $DIRECTORY
+cd ..
+
+echo $PWD
+echo "----------------------------------------"
+
+TEMP_FILENAME=".check_style_temp.`date +%s%N`"
+
+TARGET_FILES="`find -iname *.cpp -or -iname *.h -or -iname *.c | grep -vi \/tests\/ | grep -vi \/test\/ | grep -vi 3rdParty | grep -vi \/LayerManagerExamples\/ | grep -vi \config\/`"
+
+for t_file in $TARGET_FILES
+do
+    python scripts/check_all_styles.py $t_file | tee $TEMP_FILENAME -a
+done
+
+LINES=`cat $TEMP_FILENAME | wc -l`
+
+echo -e "\nTotal number of warnings: $LINES"
+rm $TEMP_FILENAME
\ No newline at end of file
diff --git a/scripts/check_tabbing_and_spacing.py b/scripts/check_tabbing_and_spacing.py
new file mode 100755 (executable)
index 0000000..eb04529
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+import sys, re, string
+from common_modules.common import *
+
+def check_tabs_no_spaces(filename, file_lines):
+    for i in range(len(file_lines)):
+        if file_lines[i].count("\t") > 0:
+            log_warning(filename, i + 1, "tab character (\\t) found, use 4 spaces instead", file_lines[i].strip(" "))
+
+def check_correct_space_count(filename, file_lines):
+    in_multiline_comment = False
+    for i in range(len(file_lines)):
+        if in_multiline_comment:
+            if file_lines[i].count("*/") > 0:
+                in_multiline_comment = file_lines[i].count("/*") > 0 and file_lines[i].index("/*") > file_lines[i].index("*/") 
+        else:
+            in_multiline_comment = file_lines[i].count("/*") > 0
+            
+            found_match = re.search("(?! )", file_lines[i])
+            if found_match:
+                space_count = found_match.start()
+                if space_count % 4 != 0:
+                    log_warning(filename, i + 1, "number of spaces at beginning of line must be divisible by 4")
+
+
+def check_no_spacing_line_end(filename, file_lines):
+    for i in range(len(file_lines)):
+        if re.search(" $", file_lines[i]):
+            log_warning(filename, i + 1, "unneeded space(s) at end of line")
+
+def check_tabbing_and_spacing(filename, file_lines):
+    check_tabs_no_spaces(filename, file_lines)
+    check_correct_space_count(filename, file_lines)
+    check_no_spacing_line_end(filename, file_lines)
+
+
+if __name__ == "__main__":
+    targets = sys.argv[1:]
+    targets = get_all_files(targets)
+
+    if len(targets) == 0:
+        print """
+\t**** No input provided ***
+\tTakes a list of files/directories as input and performs specific style checking on all files/directories.
+
+\tGives warnings if a line contains unneeded spaces at end of line, contains tab characters (\\t)
+\tor if spaces at line beginning are not divisible by 4.
+"""
+        exit(0)
+    
+    for t in targets:
+        if t[-2:] == ".h" or t[-4:] == ".cpp" or t[-2] == ".c":
+            _, _, file_lines, _ = read_file(t)
+            check_tabbing_and_spacing(t, file_lines)
diff --git a/scripts/common_modules/__init__.py b/scripts/common_modules/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/scripts/common_modules/common.py b/scripts/common_modules/common.py
new file mode 100755 (executable)
index 0000000..70d42b8
--- /dev/null
@@ -0,0 +1,140 @@
+#!/usr/bin/python
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+"""
+    Contains utility functions used by other modules
+"""
+
+import sys, re, string, os
+import config
+
+def clean_string_from_regex(s, regex, marker):
+    """
+        Removes all occurances of the passed regex from a string s, and puts instances of marker at the beginnings and ends
+        of remved substring
+    """
+
+    result = s
+    found_match = re.search(regex, result)
+
+    while found_match != None:
+        matched_string = result[found_match.start(): found_match.end()]
+        #replace by x...x where the ... is the \n chars in the matched string (to keep number of lines constant)
+        result = result[:found_match.start()] + marker + "\n" * matched_string.count("\n") + marker + result[found_match.end():]
+
+        found_match = re.search(regex, result)
+
+    return result
+
+def get_clean_file_contents(file_contents):
+    """
+        Removes strings, C style and C++ comments style comments and macros from file_contents
+    """
+    
+    clean_file_contents = file_contents
+    
+    #remove all strings
+    #a string is something that starts with a (") and ends with a ("), can have any character inside it except a (") that is not preceded by a slash (\) that is not preceded by another slash
+    #this means a string can contains a \\ escape sequence
+    #a string can contain a \" escape sequence
+    #a string can end with  \\", but it does not end with \"
+    string_re = re.compile(r'"((?!((?<!((?<!\\)\\))")).)*"', re.DOTALL)
+    
+    #'x' is passed as a marker, to mark the beginning and of strings
+    #to preserve spacing around string beginning and end
+    clean_file_contents = clean_string_from_regex(clean_file_contents, string_re, 'x')
+
+    #remove character literals
+    #a character literal is defined like a string (replacing single quotes with double quotes)
+    #this is a big relaxation of the problem for C/C++
+    char_re = re.compile(r'\'((?!((?<!((?<!\\)\\))\')).)*\'')
+
+    clean_file_contents = clean_string_from_regex(clean_file_contents, char_re, 'x')
+    
+    #remove multi-line comments
+    ml_comment_re = re.compile(r'(\s*)/\*((?!\*/).)*\*/', re.DOTALL)
+    clean_file_contents = clean_string_from_regex(clean_file_contents, ml_comment_re, '')
+    
+    #remove single line comments
+    sl_comment_re = re.compile(r'[ \t\r\f]*//.*$', re.MULTILINE)
+    clean_file_contents = re.sub(sl_comment_re, '', clean_file_contents)
+
+    #remove single-line and multi-line macros
+    #a macro is #define statement followed by a few statements that have a slash in the line before it
+    macro_re = re.compile(r'#define(\s+)(((?!\\).)+)((\\\n(((?!\\).)+))*)')
+    clean_file_contents = clean_string_from_regex(clean_file_contents, macro_re, '')
+    
+    return clean_file_contents
+
+
+def read_file(filename):
+    """
+        Reads contents of the file, returns a 4-tuple containing:
+        1- raw file contents
+        2- file contents without comments, strings and macros
+        3- raw file contents as lines
+        4- file contents without comments, strings and macros as lines
+    """
+    file_contents = open(filename).read()
+    clean_file_contents = get_clean_file_contents(file_contents)
+    
+    file_lines = file_contents.split("\n")
+    clean_file_lines = clean_file_contents.split("\n")
+    
+    return (file_contents, clean_file_contents, file_lines, clean_file_lines)
+
+
+def get_all_files(targets):
+    """
+        Iterates over targets and gets names of all files
+    """
+    filenames = []
+    for t in targets:
+        #if directory: handle every file
+        if os.path.isdir(t):
+            for (path, _, files) in os.walk(t, followlinks=True):
+                for f in files:
+                    filenames.append(os.path.abspath(os.path.join(path, f)))
+            
+        #if file: process directly
+        else:
+            filenames.append(os.path.abspath(t))
+    
+    return filenames
+
+def log_warning(filename, line_number, description, line_content = None):
+    """
+        Prints a warning to the standard output
+    """
+    if line_content == None:
+        print "{0}:{1}: warning: {2}".format(filename, line_number, description)
+    else:
+        print "{0}:{1}: warning: {2}: {3}".format(filename, line_number, description, line_content)
+    
+    config.G_WARNING_COUNT += 1
+
+def get_warning_count():
+    """
+        Returns the number of reported warnings
+    """
+    return config.G_WARNING_COUNT
+
+
+
diff --git a/scripts/common_modules/config.py b/scripts/common_modules/config.py
new file mode 100755 (executable)
index 0000000..106e808
--- /dev/null
@@ -0,0 +1,25 @@
+
+###########################################################################
+#
+# Copyright 2013 BMW Car IT GmbH
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#        http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+###########################################################################
+
+"""
+    Contains configurations constants
+"""
+G_MAX_INDENTATION_THRESHOLD = 6
+G_MAX_LINE_LENGTH_THRESHOLD = 140
+G_WARNING_COUNT = 0