mirror of https://gerrit.osmocom.org/osmo-ci
141 lines
4.1 KiB
Python
Executable File
141 lines
4.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
__doc__ = '''
|
|
With regex magic, try to pinpoint all LOG* macro calls that lack a final newline.
|
|
Also find those that have non-printable characters or extra newlines.
|
|
|
|
Usage:
|
|
|
|
./verify_log_statements.py [-d|--debug] [dir] [file] [...]
|
|
|
|
Without args, default to '.'
|
|
'''
|
|
|
|
import re
|
|
import sys
|
|
import codecs
|
|
import os.path
|
|
|
|
# This regex matches the entire LOGxx(...) statement over multiple lines.
|
|
# It pinpoints the format string by looking for the first arg that contains quotes.
|
|
# It then matches any number of separate quoted strings, and accepts 0 or more args after that.
|
|
log_statement_re = re.compile(r'^[ \t]*LOG[_A-Z]+\(([^";,]*,)*[ \t\r\n]*(("[^"]*"[^";,]*)*)(,[^;]*|)\);',
|
|
re.MULTILINE | re.DOTALL)
|
|
fmt_re = re.compile(r'("[^"]*".*)*fmt')
|
|
osmo_stringify_re = re.compile("OSMO_STRINGIFY[_A-Z]*\([^)]*\)")
|
|
|
|
debug = ('-d' in sys.argv) or ('--debug' in sys.argv)
|
|
|
|
args = [x for x in sys.argv[1:] if not (x == '-d' or x == '--debug')]
|
|
if not args:
|
|
args = ['.']
|
|
|
|
class error_found:
|
|
def __init__(self, f, charpos, msg, text):
|
|
self.f = f
|
|
self.charpos = charpos
|
|
self.msg = msg
|
|
self.text = text
|
|
self.line = None
|
|
|
|
def make_line_idx(file_content):
|
|
line_idx = []
|
|
pos = 0
|
|
line_nr = 1
|
|
line_idx.append((pos, line_nr))
|
|
for line in file_content.split('\n'):
|
|
pos += len(line)
|
|
line_nr += 1
|
|
line_idx.append((pos, line_nr))
|
|
pos += 1 # newline char
|
|
return line_idx
|
|
|
|
def char_pos_2_line(line_idx, sorted_char_positions):
|
|
r = []
|
|
line_i = 0
|
|
next_line_i = 1
|
|
for char_pos in sorted_char_positions:
|
|
while (line_i+1) < len(line_idx) and char_pos > line_idx[line_i+1][0]:
|
|
line_i += 1
|
|
r.append(line_idx[line_i][1])
|
|
return r
|
|
|
|
def check_file(f):
|
|
if not (f.endswith('.h') or f.endswith('.c') or f.endswith('.cpp')):
|
|
return []
|
|
|
|
try:
|
|
errors_found = []
|
|
|
|
file_content = codecs.open(f, "r", "utf-8").read()
|
|
|
|
for log in log_statement_re.finditer(file_content):
|
|
quoted = log.group(2)
|
|
|
|
# Skip 'LOG("bla" fmt )' strings that typically appear as #defines.
|
|
if fmt_re.match(quoted):
|
|
if debug:
|
|
errors_found.append(error_found(f, log.start(), 'Skipping define', log.group(0)))
|
|
continue
|
|
|
|
# Drop PRI* parts of 'LOG("bla %"PRIu64" foo")'
|
|
for n in (16,32,64):
|
|
quoted = quoted.replace('PRIu' + str(n), '')
|
|
quoted = quoted.replace('PRId' + str(n), '')
|
|
quoted = ''.join(osmo_stringify_re.split(quoted))
|
|
|
|
# Use py eval to join separate string constants: drop any tabs/newlines
|
|
# that are not in quotes, between separate string constants.
|
|
try:
|
|
quoted = eval('(' + quoted + '\n)' )
|
|
except:
|
|
# hopefully eval broke because of some '## args' macro def
|
|
if debug:
|
|
ignored.append(error_found(f, log.start(), 'Ignoring', log.group(0)))
|
|
continue
|
|
|
|
# check for errors...
|
|
|
|
# final newline
|
|
if not quoted.endswith('\n'):
|
|
errors_found.append(error_found(f, log.start(), 'Missing final newline', log.group(0)))
|
|
|
|
# disallowed chars and extra newlines
|
|
for c in quoted[:-1]:
|
|
if not c.isprintable() and not c == '\t':
|
|
if c == '\n':
|
|
msg = 'Extraneous newline'
|
|
else:
|
|
msg = 'Illegal char'
|
|
errors_found.append(error_found(f, log.start(), msg + ' %r' % c, log.group(0)))
|
|
|
|
if not error_found:
|
|
return []
|
|
|
|
line_idx = make_line_idx(file_content)
|
|
for r, line in zip(errors_found, char_pos_2_line(line_idx, [rr.charpos for rr in errors_found])):
|
|
r.line = line
|
|
|
|
return errors_found
|
|
except:
|
|
print("ERROR WHILE PROCESSING %r" % f, file=sys.stderr)
|
|
raise
|
|
|
|
all_errors_found = []
|
|
for f in args:
|
|
if os.path.isdir(f):
|
|
for parent_path, subdirs, files in os.walk(f, None, None):
|
|
for ff in files:
|
|
all_errors_found.extend(check_file(os.path.join(parent_path, ff)))
|
|
else:
|
|
all_errors_found.extend(check_file(f))
|
|
|
|
def print_errors(errs):
|
|
for err in errs:
|
|
print('%s: %s:%d\n%s\n' % (err.msg, err.f, err.line or 0, err.text))
|
|
|
|
print_errors(all_errors_found)
|
|
|
|
sys.exit(len(all_errors_found))
|
|
|
|
# vim: tabstop=2 shiftwidth=2 expandtab
|