624 lines
22 KiB
Python
Executable File
624 lines
22 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import re
|
|
import os
|
|
from report import perr
|
|
from common import checkTrailingWhitespace
|
|
from common import check80cols
|
|
|
|
def validateString(path, content, start, lign):
|
|
current = start + 1
|
|
contentSize = len(content)
|
|
while current < contentSize and (content[current] != '"' or content[current - 1] == '\\'):
|
|
if content[current] == '\n':
|
|
perr(path, "Wrap in string", lign)
|
|
|
|
current += 1
|
|
|
|
return (current - start)
|
|
|
|
|
|
def validatePreprocDirective(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
eolAlign = -1 #Alignement de la fin de ligne
|
|
state = 0
|
|
directive = "" #The name of the directive
|
|
restol = "" #The rest of the line
|
|
ident = 0;
|
|
while current < contentSize and (content[current] != '\n' or content[current - 1] == '\\'):
|
|
current += 1
|
|
if content[current] == '\n' and content[current - 1] == '\\':
|
|
lign += 1
|
|
cOnLine = -1
|
|
if content[current - 1] == '\t':
|
|
perr(path, "Tabulation are baned!", lign)
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 8 + 1
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
elif content[current] != '\n':
|
|
cOnLine += 1
|
|
|
|
#Check indentation
|
|
if state == 0:
|
|
if content[current] != ' ' and content[current] != '\t':
|
|
state += 1
|
|
ident = current - start - 1
|
|
|
|
#Wrap
|
|
if content[current] == '\\':
|
|
if eolAlign <= 0:
|
|
eolAlign = cOnLine
|
|
elif cOnLine != eolAlign:
|
|
perr(path, "Bad endline preprocessor alignement (expected: {0}, real: {1})".format(eolAlign, cOnLine), lign)
|
|
|
|
#Spaces state
|
|
if state == 2 or state == 4 or state == 6 or state == 8:
|
|
if content[current] != ' ' and content[current] != '\t':
|
|
state += 1
|
|
|
|
#Searching the directive name
|
|
if state == 1:
|
|
if content[current] != ' ' and content[current] != '\t':
|
|
directive += content[current]
|
|
else:
|
|
state += 1
|
|
|
|
#Validate Macro Name
|
|
if state == 3:
|
|
if content[current] != ' ' and content[current] != '\t' and content[current] != '(':
|
|
restol += content[current]
|
|
else:
|
|
state += 1
|
|
if re.match("^[A-Z0-9_]$", restol) is None:
|
|
perr(path, "Preprocessor macro name not fully capitalized or contain invalid chars ({0})".format(restol), lign)
|
|
restol = ""
|
|
|
|
#Validate Macro Var
|
|
if state == 5:
|
|
if content[current] != ' ' and content[current] != '\t' and content[current] != ',':
|
|
restol += content[current]
|
|
else:
|
|
if content[current] != ')':
|
|
state += 1
|
|
if restol != "" and re.match("^[A-Z][a-z0-9_]$", restol) is None:
|
|
perr(path, "Preprocessor macro variable name not fully capitalized or contains invalid chars ({0})".format(restol), lign)
|
|
restol = ""
|
|
|
|
if directive.find("endif") == 0:
|
|
states['preproc-ident'] -= 1
|
|
|
|
if ident != states['preproc-ident']:
|
|
perr(path, "Bad preprocessor indentation (expected: {0}, real: {1})".format(states['preproc-ident'], (ident)), lign)
|
|
|
|
if directive.find("if") == 0:
|
|
states['preproc-ident'] += 1
|
|
|
|
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
def validateType(path, content, current, lign, states):
|
|
content += " " #Add a sentinel
|
|
contentSize = len(content)
|
|
state = 0
|
|
while current + 1 < contentSize:
|
|
if content[current] == '*' and content[current - 1] != ' ':
|
|
perr(path, "No space before *", lign)
|
|
elif content[current] == '=' and content[current - 1] != ' ':
|
|
perr(path, "No space before =", lign)
|
|
elif content[current] == '=' and content[current + 1] != ' ':
|
|
perr(path, "No space after =", lign)
|
|
elif content[current] == '=':
|
|
state = 1
|
|
elif state == 1:
|
|
if content[current] == '*' and content[current + 1] != ' ':
|
|
perr(path, "No space after *", lign)
|
|
elif content[current] == '+' and content[current + 1] != ' ':
|
|
perr(path, "No space after +", lign)
|
|
elif content[current] == '+' and content[current - 1] != ' ':
|
|
perr(path, "No space before +", lign)
|
|
elif content[current] == '-' and content[current + 1] != ' ':
|
|
perr(path, "No space before -", lign)
|
|
elif content[current] == '-' and content[current - 1] != ' ':
|
|
perr(path, "No space before -", lign)
|
|
elif content[current] == '/' and content[current + 1] != ' ':
|
|
perr(path, "No space before /", lign)
|
|
elif content[current] == '/' and content[current - 1] != ' ':
|
|
perr(path, "No space before /", lign)
|
|
elif content[current] == '%' and content[current + 1] != ' ':
|
|
perr(path, "No space before %", lign)
|
|
elif content[current] == '%' and content[current - 1] != ' ':
|
|
perr(path, "No space before %", lign)
|
|
elif content[current] == '(' and content[current - 1] != ' ':
|
|
perr(path, "Warning: it seems there is a function calling with a declaration", lign)
|
|
|
|
current += 1
|
|
|
|
def validateStruct(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
varAlign = -1
|
|
state = 0
|
|
lastBlank = 0
|
|
typeName = ""
|
|
strEc = ""
|
|
while current < contentSize and content[current] != '}':
|
|
if content[current] == ' ' and content[current + 1] != ';':
|
|
typeName += strEc + ' '
|
|
strEc = ""
|
|
else:
|
|
strEc += content[current]
|
|
|
|
#Search the struct begining
|
|
if state == 0:
|
|
if content[current] == '{':
|
|
state = 1
|
|
cOnLine = 0
|
|
strEc = ""
|
|
typeName = ""
|
|
|
|
if state == 1:
|
|
if content[current] == ' ' or content[current] == '\t':
|
|
lastBlank = cOnLine
|
|
elif content[current] == '\n' and lastBlank != 0:
|
|
#Check type
|
|
validateType(path, typeName.strip().rstrip(), 0, start_lign, states)
|
|
strEc = ""
|
|
typeName = ""
|
|
|
|
#Check variable alignment
|
|
if varAlign == -1:
|
|
varAlign = lastBlank
|
|
elif lastBlank != varAlign:
|
|
perr(path, "Bad variable alignement in a struct (expected: {0}, real: {1})".format(varAlign, lastBlank), lign)
|
|
elif content[current] == ',':
|
|
perr(path, "Two variable declarations aren't allowed on the same line", lign)
|
|
elif content[current] == ';' and content[current - 1] == ' ':
|
|
perr(path, "There are a space before ;", lign)
|
|
|
|
if content[current] == '\n':
|
|
lign += 1
|
|
cOnLine = -1
|
|
lastBlank = 0
|
|
current += 1
|
|
if content[current - 1] == '\t':
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 9
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
else:
|
|
cOnLine += 1
|
|
|
|
while current < contentSize and content[current] != ';':
|
|
cOnLine += 1
|
|
current += 1
|
|
if content[current] == ';' and content[current - 1] == ' ':
|
|
perr(path, "There is a space before ;", lign)
|
|
|
|
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
def validateUnion(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
varAlign = -1
|
|
state = 0
|
|
lastBlank = 0
|
|
typeName = ""
|
|
strEc = ""
|
|
while current < contentSize and content[current] != '}':
|
|
if content[current] == ' ' and content[current + 1] != ';':
|
|
typeName += strEc + ' '
|
|
strEc = ""
|
|
else:
|
|
strEc += content[current]
|
|
|
|
#Search the union begining
|
|
if state == 0:
|
|
if content[current] == '{':
|
|
state = 1
|
|
cOnLine = 0
|
|
strEc = ""
|
|
typeName = ""
|
|
|
|
if state == 1:
|
|
if content[current] == ' ' or content[current] == '\t':
|
|
lastBlank = cOnLine
|
|
elif content[current] == '\n' and lastBlank != 0:
|
|
#Check type
|
|
validateType(path, typeName.strip().rstrip(), 0, start_lign, states)
|
|
strEc = ""
|
|
typeName = ""
|
|
|
|
#Check variable alignment
|
|
if varAlign == -1:
|
|
varAlign = lastBlank
|
|
elif lastBlank != varAlign:
|
|
perr(path, "Bad variable alignement in an union (expected: {0}, real: {1})".format(varAlign, lastBlank), lign)
|
|
elif content[current] == ',':
|
|
perr(path, "Two variable declarations aren't allowed on the same line", lign)
|
|
elif content[current] == ';' and content[current - 1] == ' ':
|
|
perr(path, "There is a space before ;", lign)
|
|
|
|
if content[current] == '\n':
|
|
lign += 1
|
|
cOnLine = -1
|
|
lastBlank = 0
|
|
current += 1
|
|
if content[current - 1] == '\t':
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 9
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
else:
|
|
cOnLine += 1
|
|
|
|
while current < contentSize and content[current] != ';':
|
|
cOnLine += 1
|
|
current += 1
|
|
if content[current] == ';' and content[current - 1] == ' ':
|
|
perr(path, "There is a space before ;", lign)
|
|
|
|
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
def validateEnum(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
varAlign = -1
|
|
state = 0
|
|
while current < contentSize and content[current] != '}':
|
|
#Search the enum begining
|
|
if state == 0:
|
|
if content[current] == '{':
|
|
state = 1
|
|
cOnLine = 0
|
|
|
|
if state == 1:
|
|
if content[current] == '=' and content[current + 1] != ' ':
|
|
perr(path, "Missing space after =", lign)
|
|
if content[current] == '=' and content[current - 1] != ' ':
|
|
perr(path, "Missing space before =", lign)
|
|
if content[current] == ',' and content[current + 1] != '\n':
|
|
perr(path, "Two statements on the same line", lign)
|
|
|
|
if content[current] == '\n':
|
|
lign += 1
|
|
cOnLine = -1
|
|
if content[current - 1] == '\t':
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 9
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
else:
|
|
cOnLine += 1
|
|
current += 1
|
|
|
|
while current < contentSize and content[current] != ';':
|
|
cOnLine += 1
|
|
current += 1
|
|
if content[current] == ';' and content[current - 1] == ' ':
|
|
perr(path, "There is a space before ;", lign)
|
|
|
|
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
|
|
def validateTypedef(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
newname = "" #The name of the typedef
|
|
restol = "" #The rest of the line
|
|
state = 0
|
|
while current < contentSize and (content[current] != ';' or state > 0):
|
|
current += 1
|
|
if content[current] == '\n':
|
|
lign += 1
|
|
cOnLine = -1
|
|
if content[current - 1] == '\t':
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 9
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
else:
|
|
cOnLine += 1
|
|
|
|
if content[current] == '{':
|
|
state += 1
|
|
elif content[current] == '}':
|
|
state -= 1
|
|
|
|
if content[current] == ' ' and content[current + 1] != ';':
|
|
restol += newname + ' '
|
|
newname = ""
|
|
else:
|
|
newname += content[current]
|
|
|
|
if content[current - 1] == ' ' and content[current] == ';':
|
|
perr(path, "There is a space before ;", lign)
|
|
|
|
restol = restol.strip().rstrip();
|
|
if restol.find("struct") == 0:
|
|
validateStruct(path, restol + ";", 0, start_lign, states)
|
|
if newname[0] != 's' or newname[1] != '_':
|
|
perr(path, "Bad typedef name for a struct (expected: s_; real: {0}{1})".format(newname[0], newname[1]), lign)
|
|
elif restol.find("union") == 0:
|
|
validateUnion(path, restol + ";", 0, start_lign, states)
|
|
if newname[0] != 'u' or newname[1] != '_':
|
|
perr(path, "Bad typedef name for an union (expected: u_; real: {0}{1})".format(newname[0], newname[1]), lign)
|
|
elif restol.find("enum") == 0:
|
|
validateEnum(path, restol + ";", 0, start_lign, states)
|
|
if newname[0] != 'e' or newname[1] != '_':
|
|
perr(path, "Bad typedef name for an enum (expected: e_; real: {0}{1})".format(newname[0], newname[1]), lign)
|
|
else:
|
|
validateType(path, restol, 0, start_lign, states)
|
|
if newname[0] != 't' or newname[1] != '_':
|
|
perr(path, "Bad typedef name (expected: t_; real: {0}{1})".format(newname[0], newname[1]), lign)
|
|
|
|
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
|
|
def validateFor(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
stack = -1
|
|
while current < contentSize and (content[current] != ')' or stack >= 0):
|
|
current += 1
|
|
if content[current] == '\n':
|
|
lign += 1
|
|
cOnLine = -1
|
|
if content[current - 1] == '\t':
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 9
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
elif content[current] != '\n':
|
|
cOnLine += 1
|
|
|
|
if content[current] == ';':
|
|
if content[current - 1] == ' ' and content[current - 2] != ';':
|
|
perr(path, "There is a space before ;", lign)
|
|
if content[current + 1] != ' ':
|
|
perr(path, "There is no space after ;", lign)
|
|
|
|
if content[current] == '(':
|
|
stack += 1
|
|
elif content[current] == ')':
|
|
stack -= 1
|
|
|
|
if content[current] == ')':
|
|
if content[current - 1] == ' ' and content[current - 2] != ';':
|
|
perr(path, "There is a space before )", lign)
|
|
if content[current - 1] == ';':
|
|
perr(path, "There is no space before ), expected one in this case", lign)
|
|
|
|
eaten = { 'chars': current - start + 1, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
|
|
def validateComment(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
while current + 1 < contentSize and content[current] != '*' and content[current + 1] != '/':
|
|
current += 1
|
|
|
|
eaten = { 'chars': current - start - 1, 'lines': lign }
|
|
return eaten
|
|
|
|
|
|
def validateFunction(path, content, start, start_lign, states, prototype):
|
|
lign = start_lign
|
|
nbLign = 0
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
stack = 0
|
|
while current < contentSize and (content[current] != '}' or stack >= 0):
|
|
current += 1
|
|
if content[current] == '\n':
|
|
if cOnLine > 0:
|
|
nbLign += 1
|
|
lign += 1
|
|
cOnLine = -1
|
|
if content[current - 1] == '\t':
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 9
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
elif content[current] != '\n':
|
|
cOnLine += 1
|
|
|
|
#String
|
|
if content[current] == '"':
|
|
eatenChar = validateString(path, content, current, lign)
|
|
current += eatenChar
|
|
cOnLine += eatenChar
|
|
|
|
if content[current - 2] == 'f' and content[current - 1] == 'o' and content[current] == 'r':
|
|
eaten = validateFor(path, content, current, lign, states)
|
|
cOnLine = eaten['col'];
|
|
current += eaten['chars'];
|
|
lign = eaten['lines'];
|
|
|
|
if content[current - 1] == ';' and content[current] != '\n':
|
|
perr(path, "There is a ; without new line after", lign)
|
|
|
|
if content[current] == '{':
|
|
stack += 1
|
|
elif content[current] == '}':
|
|
stack -= 1
|
|
|
|
#Skip lines containing comments in the count
|
|
if content[current] == "/" == content[current - 1]:
|
|
nbLign -= 1
|
|
elif content[current] == "/" and content[current - 1] == "*":
|
|
eaten = validateComment(path, content, current, lign, states)
|
|
current += eaten['chars']
|
|
lign += eaten['lines']
|
|
|
|
if nbLign > 25:
|
|
perr(path, "To many line in this function: {0}".format(nbLign), start_lign)
|
|
#else:
|
|
# perr(path, "This function is a {0} lines function, good :)".format(nbLign), start_lign)
|
|
|
|
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
|
|
def validateVarOrProto(path, content, start, start_lign, states):
|
|
return 1
|
|
|
|
|
|
def validateFunOrGlobal(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
restol = "" #The rest of the line
|
|
while current < contentSize and content[current] != ';' and content[current] != '{':
|
|
current += 1
|
|
if content[current] == '\n':
|
|
lign += 1
|
|
cOnLine = -1
|
|
if content[current - 1] == '\t':
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 9
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
elif content[current] != '\n':
|
|
cOnLine += 1
|
|
|
|
if content[current] != '\n':
|
|
restol += content[current]
|
|
|
|
if content[current] == '{':
|
|
eaten = validateFunction(path, content, current, lign, states, restol)
|
|
|
|
current += eaten['chars']
|
|
cOnLine = eaten['col']
|
|
lign = eaten['lines']
|
|
else:
|
|
eaten = validateVarOrProto(path, content, current, lign, states)
|
|
|
|
eaten = { 'chars': current - start, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
|
|
def validateExternalWord(path, content, start, start_lign, states):
|
|
lign = start_lign
|
|
current = start
|
|
contentSize = len(content)
|
|
cOnLine = 0
|
|
directive = "" #The name of the directive
|
|
while current < contentSize and content[current] != '\n':
|
|
#Search keyword
|
|
if content[current] != ' ' and content[current] != '\t':
|
|
directive += content[current]
|
|
else:
|
|
if directive == "typedef":
|
|
eaten = validateTypedef(path, content, current, lign, states)
|
|
elif directive == "struct":
|
|
eaten = validateStruct(path, content, current, lign, states)
|
|
elif directive == "enum":
|
|
eaten = validateEnum(path, content, current, lign, states)
|
|
elif directive == "union":
|
|
eaten = validateUnion(path, content, current, lign, states)
|
|
else:
|
|
eaten = validateFunOrGlobal(path, content, current, lign, states)
|
|
|
|
current += eaten['chars']
|
|
cOnLine = eaten['col']
|
|
lign = eaten['lines']
|
|
|
|
current += 1
|
|
if content[current - 1] == '\t':
|
|
if cOnLine % 8 == 0:
|
|
cOnLine += 9
|
|
else:
|
|
cOnLine += cOnLine % 8 + 1
|
|
else:
|
|
cOnLine += 1
|
|
|
|
|
|
eaten = { 'chars': current - start - 1, 'lines': lign, 'col': cOnLine }
|
|
return eaten
|
|
|
|
|
|
def checkFile(path, opt):
|
|
numLign = 1
|
|
with open(path, "r") as fp:
|
|
content = fp.read()
|
|
current = 0;
|
|
cOnLine = 0
|
|
last_nblank = 0
|
|
contentSize = len(content)
|
|
states = {
|
|
'preproc-ident': 0 #Niveau d'identation dans le preprocesseur
|
|
}
|
|
|
|
while current < contentSize:
|
|
if content[current] == '\n':
|
|
numLign += 1
|
|
last_nblank = 0
|
|
cOnLine = -1
|
|
|
|
#Preprocessor line
|
|
if last_nblank == cOnLine and content[current] == '#':
|
|
if cOnLine != 0:
|
|
perr(path, "Preprocessor directive mark not on the first column", numLign)
|
|
eaten = validatePreprocDirective(path, content, current, numLign, states)
|
|
current += eaten['chars']
|
|
cOnLine = eaten['col']
|
|
numLign = eaten['lines']
|
|
|
|
#String
|
|
elif content[current] == '"':
|
|
print "str"
|
|
eatenChar = validateString(path, content, current, numLign)
|
|
current += eatenChar
|
|
cOnLine += eatenChar
|
|
|
|
#Comments
|
|
elif content[current] == '*' and content[current - 1] == '/':
|
|
eaten = validateComment(path, content, current, numLign, states)
|
|
current += eaten['chars']
|
|
cOnLine = 0
|
|
numLign = eaten['lines']
|
|
|
|
#Skip empty lines
|
|
elif content[current] != '\n':
|
|
eaten = validateExternalWord(path, content, current, numLign, states)
|
|
current += eaten['chars']
|
|
cOnLine = eaten['col']
|
|
numLign = eaten['lines']
|
|
|
|
if last_nblank == cOnLine and content[current] == ' ' or content[current] == '\t':
|
|
last_nblank += 1
|
|
current += 1
|
|
cOnLine += 1
|
|
|
|
#print("{1} contient {0} lignes".format(numLign, os.path.basename(path)))
|
|
|
|
import sys
|
|
|
|
if __name__ == "__main__":
|
|
i = 1
|
|
while i < len(sys.argv):
|
|
checkFile(sys.argv[i], dict())
|
|
i += 1
|