This repository has been archived on 2021-03-01. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
moulinette/moulinette.py

160 lines
5.8 KiB
Python
Executable file

#!/usr/bin/python
import signal
import sys
import os
import re
if len(sys.argv) < 2:
print("Usage:\t{0} FILE...".format(sys.argv[0]))
sys.exit(1)
def printError(file, type, line = -1):
if line >= 0:
print("{0} in {1}:{2}".format(type, file, line))
else:
print("{0} for {1}".format(type, file))
def checkFile(path):
if os.path.isdir(path):
checkDir(path)
else:
baseName = os.path.basename(path)
if baseName.upper() == "AUTHORS":
if baseName != "AUTHORS":
printError(path, "Bad filename")
checkAuthors(path)
if baseName[len(baseName)-1] == '~' or baseName[len(baseName)-1] == '#'\
or baseName[0] == '#':
printError(path, "This is a temporary file")
iExt = baseName.rfind('.')
if iExt > 0:
ext = baseName[iExt:]
if ext == ".c":
checkCFile(path)
elif ext == ".h":
checkHFile(path)
elif ext == ".sh":
checkSHFile(path)
else:
checkOtherFile(path)
def checkDir(path):
dirList = os.listdir(path)
filesList = []
for f in dirList:
basename = os.path.basename(f)
if os.path.isdir(f):
if basename == ".git" or basename == ".hg":
printError(path, "There is a {0} repository".format(basename))
else:
if basename[0] == '.':
printError(path + '/' + basename, "Hidden directory")
checkDir(path + '/' + f)
else:
if basename[0] == '.':
printError(path + '/' + basename, "Hidden file")
checkFile(path + '/' + f)
def checkCComment(path, line, num):
txt = line.strip()
if re.match("\*/", txt) is not None:
if re.match("\*/$", txt) is None:
printError(path, "Last big comment lines aren't empty", num)
return 0
elif re.match("\*/", txt) is not None and \
re.match("/\*", txt) is None:
printError(path, "\*/ ", num)
elif re.match("^\*\* ", txt) is None:
printError(path, "Bad intermediary comment lines", num)
return 1
def checkCLang(path, line, num):
txt = line.strip()
if re.match("^#define", txt) is not None and \
re.match("^#define +[A-Z0-9_]+[ (]?", txt) is None:
printError(path, "Constant or macro without full uppercase", num)
elif re.match("^ +#", line) is not None:
printError(path, "The preprocessor directive mark not on the first column", num)
elif re.match(".*sizeof[^ ].*", txt) is not None:
printError(path, "Forgotten space after keyword", num)
elif re.match("^# *(else|endif)", txt) is not None and \
re.match("^# *(else|endif) +/", txt) is None:
printError(path, "Preprocesor condition without comment describing the corresponding condition", num)
elif re.match("^(typedef +)?struct", txt) is not None and \
re.match("^(typedef +)?struct +s_", txt) is None:
printError(path, "New struct without s_ name", num)
elif re.match("^(typedef +)?union", txt) is not None and \
re.match("^(typedef +)?union +u_", txt) is None:
printError(path, "New union without u_ name", num)
elif re.match("^(typedef +)?enum", txt) is not None and \
re.match("^(typedef +)?enum +e_", txt) is None:
printError(path, "New struct without s_ name", num)
elif re.match("^typedef *(^(union|struct|enum))", txt) is not None and \
re.match("^typedef +(unsigned )?[a-z_\*] +t_", txt) is None:
printError(path, "New type without t_ name", num)
elif re.match("^(if|else|elseif|for|while|do|typedef|struct|return|sizeof)", txt) is not None and \
re.match("^(if|else|elseif|for|while|do|typedef|struct|return|sizeof)(uble| |$)", txt) is None:
printError(path, "Forgotten space after keyword", num)
elif re.match("^(if|else|elseif|for|while|do|typedef|struct)", txt) is not None and \
re.match("^(if|else|elseif|for|while|do|typedef|struct).*\{", txt) is not None:
printError(path, "{ on the same line of instruction", num)
elif re.match("(^/\*|\*/$)", txt) is not None and \
re.match("^(/\*|\*/)$", txt) is None:
printError(path, "First and last big comment lines aren't empty", num)
elif re.match("/\*$", txt) is not None:
return 1
elif re.match("^for", txt) is not None and \
re.match("^for \(.*; .*; .*\)", txt) is None:
printError(path, "Bad syntax for a for", num)
elif re.match("^return", txt) is not None and \
re.match("^return \(.*\)", txt) is None:
printError(path, "Bad syntax for return", num)
elif re.match(".*[^=]=.*;", txt) is not None and \
re.match(".* [><]?[-&%+^*/!><|]?= .*", txt) is None:
printError(path, "Bad syntax for an affectation", num)
elif re.match("\r$", txt) is not None:
printError(path, "DOS CR+LF line terminator detected", num)
return 0
def checkHFile(path):
checkCFile(path)
num = 0
with open(path, "r") as fp:
lines = fp.read().split('\n')
#Search for Header protection
for line in lines:
num += 1
line = line.replace(" ", " ")
def checkCFile(path):
num = 0
with open(path, "r") as fp:
lines = fp.read().split('\n')
typeBlock = 0
for line in lines:
num += 1
line = line.replace(" ", " ")
check80cols(path, line, num)
checkTrailingWhitespace(path, line, num)
if typeBlock == 1:
typeBlock = checkCComment(path, line, num)
else:
typeBlock = checkCLang(path, line, num)
#Define some constants
i = 1
while i < len(sys.argv):
checkFile(sys.argv[i])
i += 1