#!/usr/bin/env python # Copyright (c) 2016, 2017 Red Hat, Inc. # # 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. from __future__ import print_function import email import getopt import re import sys __errors = 0 __warnings = 0 print_file_name = None checking_file = False total_line = 0 colors = False def get_color_end(): global colors if colors: return "\033[00m" return "" def get_red_begin(): global colors if colors: return "\033[91m" return "" def get_yellow_begin(): global colors if colors: return "\033[93m" return "" def print_error(message): global __errors print("%sERROR%s: %s" % (get_red_begin(), get_color_end(), message)) __errors = __errors + 1 def print_warning(message): global __warnings print("%sWARNING%s: %s" % (get_yellow_begin(), get_color_end(), message)) __warnings = __warnings + 1 # These are keywords whose names are normally followed by a space and # something in parentheses (usually an expression) then a left curly brace. # # 'do' almost qualifies but it's also used as "do { ... } while (...);". __parenthesized_constructs = 'if|for|while|switch|[_A-Z]+FOR_EACH[_A-Z]*' __regex_added_line = re.compile(r'^\+{1,2}[^\+][\w\W]*') __regex_subtracted_line = re.compile(r'^\-{1,2}[^\-][\w\W]*') __regex_leading_with_whitespace_at_all = re.compile(r'^\s+') __regex_leading_with_spaces = re.compile(r'^ +[\S]+') __regex_trailing_whitespace = re.compile(r'[^\S]+$') __regex_single_line_feed = re.compile(r'^\f$') __regex_for_if_missing_whitespace = re.compile(r' +(%s)[\(]' % __parenthesized_constructs) __regex_for_if_too_much_whitespace = re.compile(r' +(%s) +[\(]' % __parenthesized_constructs) __regex_for_if_parens_whitespace = \ re.compile(r' +(%s) \( +[\s\S]+\)' % __parenthesized_constructs) __regex_is_for_if_single_line_bracket = \ re.compile(r'^ +(%s) \(.*\)' % __parenthesized_constructs) __regex_ends_with_bracket = \ re.compile(r'[^\s]\) {(\s+/\*[\s\Sa-zA-Z0-9\.,\?\*/+-]*)?$') __regex_ptr_declaration_missing_whitespace = re.compile(r'[a-zA-Z0-9]\*[^*]') __regex_is_comment_line = re.compile(r'^\s*(/\*|\*\s)') skip_leading_whitespace_check = False skip_trailing_whitespace_check = False skip_block_whitespace_check = False skip_signoff_check = False # Don't enforce character limit on files that include these characters in their # name, as they may have legitimate reasons to have longer lines. # # Python isn't checked as flake8 performs these checks during build. line_length_blacklist = ['.am', '.at', 'etc', '.in', '.m4', '.mk', '.patch', '.py'] def is_subtracted_line(line): """Returns TRUE if the line in question has been removed.""" return __regex_subtracted_line.search(line) is not None def is_added_line(line): """Returns TRUE if the line in question is an added line. """ global checking_file return __regex_added_line.search(line) is not None or checking_file def added_line(line): """Returns the line formatted properly by removing diff syntax""" global checking_file if not checking_file: return line[1:] return line def leading_whitespace_is_spaces(line): """Returns TRUE if the leading whitespace in added lines is spaces """ if skip_leading_whitespace_check: return True if (__regex_leading_with_whitespace_at_all.search(line) is not None and __regex_single_line_feed.search(line) is None): return __regex_leading_with_spaces.search(line) is not None return True def trailing_whitespace_or_crlf(line): """Returns TRUE if the trailing characters is whitespace """ if skip_trailing_whitespace_check: return False return (__regex_trailing_whitespace.search(line) is not None and __regex_single_line_feed.search(line) is None) def if_and_for_whitespace_checks(line): """Return TRUE if there is appropriate whitespace after if, for, while """ if skip_block_whitespace_check: return True if (__regex_for_if_missing_whitespace.search(line) is not None or __regex_for_if_too_much_whitespace.search(line) is not None or __regex_for_if_parens_whitespace.search(line)): return False return True def if_and_for_end_with_bracket_check(line): """Return TRUE if there is not a bracket at the end of an if, for, while block which fits on a single line ie: 'if (foo)'""" def balanced_parens(line): """This is a rather naive counter - it won't deal with quotes""" balance = 0 for letter in line: if letter == '(': balance += 1 elif letter == ')': balance -= 1 return balance == 0 if __regex_is_for_if_single_line_bracket.search(line) is not None: if not balanced_parens(line): return True if __regex_ends_with_bracket.search(line) is None: return False return True def pointer_whitespace_check(line): """Return TRUE if there is no space between a pointer name and the asterisk that denotes this is a apionter type, ie: 'struct foo*'""" return __regex_ptr_declaration_missing_whitespace.search(line) is not None def line_length_check(line): """Return TRUE if the line length is too long""" if len(line) > 79: return True return False def is_comment_line(line): """Returns TRUE if the current line is part of a block comment.""" return __regex_is_comment_line.match(line) is not None checks = [ {'regex': None, 'match_name': lambda x: not any([fmt in x for fmt in line_length_blacklist]), 'check': lambda x: line_length_check(x), 'print': lambda: print_warning("Line length is >79-characters long")}, {'regex': '$(?