1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
#!/usr/bin/python3
# Parses a source file, ensuring that CLI definitions (DEFUNs, ALIASs, etc)
# have install_command called on them at some point.
import sys
import glob
import re
import os
from pprint import pprint
# patterns used to extract commands
command_patterns = [
r'DEF.*\(.*\n\s*([0-9a-z_]*_cmd)',
r'ALIAS.*\(.*\n\s*([0-9a-z_]*_cmd)',
]
# patterns that count as installing the command
install_patterns = [
r'install_element.*\(.*{0}',
r'INSTALL.*\(.*{0}'
]
def process(filename):
cmds = []
uninstalled = []
sourcetext = ''
headertext = ''
# read source file and header file
with open(filename) as cf:
try:
sourcetext = cf.read()
headerfile = filename.replace('.c', '.h')
if os.path.isfile(headerfile):
with open(headerfile) as hf:
headertext = hf.read()
except:
print('Error reading {0}, skipping'.format(filename))
return
# build list of defined commands that aren't mentioned in header
for pattern in command_patterns:
matches = re.findall(pattern, sourcetext, re.M)
cmds += filter(lambda x: re.search(x, headertext) is None, matches)
# build list of not installed commands
for cmd in cmds:
pats = [ ip.format(cmd) for ip in install_patterns ]
if all([ re.search(pat, sourcetext) is None for pat in pats ]):
uninstalled.append(cmd)
if len(uninstalled) > 0:
print('\033[92m', end='')
print('{0}'.format(filename))
print('\033[0m', end='')
for cmd in uninstalled:
print(' {0}'.format(cmd))
print('')
usage = """
Usage:
./cmd_check.py <path> [<path>...]
where 'path' is a C source file or directory
containing C source files
"""
if __name__ == '__main__':
if len(sys.argv) < 2:
print(usage)
exit()
cwd = os.getcwd()
for arg in sys.argv[1:]:
# collect all c files
globstr = arg
if os.path.isdir(arg):
os.chdir(arg)
globstr = '*.c'
for filename in glob.glob(globstr):
process(filename)
os.chdir(cwd)
|