On 6/22/07, Bruno Haible <[EMAIL PROTECTED]> wrote:
3) Verify that every source file that includes gnulib headers
starts out
with #include <config.h>.
Here's a quick way of doing that (derived from the coreutils
Makefile.maint) :-
#! /usr/bin/env python
import re
import sys
FIRST_INCLUDE = 'config.h'
problems = 0
LINE_SMELLS_SRC = [
[r'(?<!\w)free \(\(', "don't cast the argument to free()"],
[r'\*\) *x(m|c|re)alloc(?!\w)',"don't cast the result of x*alloc"],
[r'\*\) *alloca(?!\w)',"don't cast the result of alloca"],
[r'[ ] ',"found SPACE-TAB; remove the space"],
[r'(?<!\w)([fs]?scanf|ato([filq]|ll))(?!\w)',
'do not use *scan''f, ato''f, ato''i, ato''l, ato''ll, ato''q, or
ss''canf'],
[r'error \(EXIT_SUCCESS',"passing EXIT_SUCCESS to error is
confusing"],
[r'file[s]ystem', "prefer writing 'file system' to 'filesystem'"],
[r'HAVE''_CONFIG_H', "Avoid checking HAVE_CONFIG_H"],
# [r'HAVE_FCNTL_H', "Avoid checking HAVE_FCNTL_H"],
[r'O_NDELAY', "Avoid using O_NDELAY"],
[r'the *the', "'the the' is probably not deliberate"],
[r'(?<!\w)error \([^_"]*[^_]"[^"]*[a-z]{3}', "untranslated error
message"],
[r'^# *if\s+defined *\(', "useless parentheses in '#if defined'"],
]
FILE_SMELLS_SRC = [
[r'# *include <assert.h>(?!.*assert \()',
"If you include <assert.h>, use assert()."],
[r'# *include "quotearg.h"(?!.*(?<!\w)quotearg(_[^ ]+)? \()',
"If you include \"quotearg.h\", use one of its functions."],
[r'# *include "quote.h"(?!.*(?<!\w)quote(_[^ ]+)? \()',
"If you include \"quote.h\", use one of its functions."],
[r'\s$', "trailing whitespace"],
]
# missing check: ChangeLog prefixes
# missing: sc_always_defined_macros from coreutils
# missing: sc_tight_scope
COMPILED_LINE_SMELLS = [(re.compile(pong[0]), pong[1])
for pong in LINE_SMELLS_SRC]
COMPILED_FILE_SMELLS = [(re.compile(pong[0], re.DOTALL), pong[1])
for pong in FILE_SMELLS_SRC]
def Problem(filename, desc):
global problems
print >> sys.stderr, "error: %s: %s" % (filename, desc)
problems += 1
def Warning(filename, desc):
print >> sys.stderr, "warning: %s: %s" % (filename, desc)
def BuildIncludeList(text):
include_re = re.compile(r'# *include +[<"](.*)[>"]')
return [m.group(1) for m in include_re.finditer(text)]
def CheckFirstInclude(filename, lines, fulltext):
includes = BuildIncludeList(fulltext)
if includes and includes[0] != FIRST_INCLUDE:
if FIRST_INCLUDE in includes:
fmt = "%s is first include file, but %s should be
included first"
Problem(filename, fmt % (includes[0], FIRST_INCLUDE))
if FIRST_INCLUDE not in includes:
Warning(filename,
"%s should be included by most files" % FIRST_INCLUDE)
def CheckLineSmells(filename, lines, fulltext):
for line in lines:
for smell in COMPILED_LINE_SMELLS:
match = smell[0].search(line[1])
if match:
Problem(filename, '%d: "%s": %s'
% (line[0], match.group(0), smell[1]))
def CheckFileSmells(filename, lines, fulltext):
for smell in COMPILED_FILE_SMELLS:
match = smell[0].search(fulltext)
if match:
Problem(filename, ' %s' % (smell[1],))
def SniffSourceFile(filename, lines, fulltext):
for sniffer in [CheckFirstInclude, CheckLineSmells,
CheckFileSmells]:
sniffer(filename, lines, fulltext)
def main(args):
"main program"
for srcfile in args[1:]:
f = open(srcfile)
line_number = 1
lines = []
for line in f.readlines():
lines.append( (line_number, line) )
line_number += 1
fulltext = '\n'.join([line[1] for line in lines])
SniffSourceFile(srcfile, lines, fulltext)
f.close()
if problems:
return 1
else:
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))