Tweak an older NEWS item to be a bit clearer.
[rsync.git] / packaging / var-checker
blobf17c69a296978c74eb8d0533acf81016722502bc
1 #!/usr/bin/env -S python3 -B
3 # This script checks the *.c files for extraneous "extern" variables,
4 # for vars that are defined but not used, and for inconsistent array
5 # sizes.  Run it from inside the main rsync directory.
7 import os, sys, re, argparse, glob
9 VARS_RE = re.compile(r'^(?!(?:extern|enum)\s)([a-zA-Z]\S*\s+.*);', re.M)
10 EXTERNS_RE = re.compile(r'^extern\s+(.*);', re.M)
12 sizes = { }
14 def main():
15     add_syscall_c = set('t_stub.c t_unsafe.c tls.c trimslash.c'.split())
16     add_util_c = set('t_stub.c t_unsafe.c'.split())
18     if not os.path.exists('syscall.c'):
19         if os.path.exists('var-checker'):
20             os.chdir('..')
21         else:
22             print("Couldn't find the source dir.")
23             sys.exit(1)
25     syscall_c = slurp_file('syscall.c', True)
26     util_c = slurp_file('util1.c', True)
28     for fn in sorted(glob.glob('*.c')):
29         txt = slurp_file(fn)
31         var_list = parse_vars(fn, VARS_RE.findall(txt))
32         extern_list = parse_vars(fn, EXTERNS_RE.findall(txt))
33         if not var_list and not extern_list:
34             continue
36         if fn in add_syscall_c:
37             txt += syscall_c
38         if fn in add_util_c:
39             txt += util_c
41         txt = re.sub(r'INFO_GTE', 'info_levels ', txt)
42         txt = re.sub(r'DEBUG_GTE', 'debug_levels ', txt)
43         txt = re.sub(r'SIGACTION\(', 'sigact (', txt)
45         find = '|'.join([ re.escape(x) for x in var_list + extern_list ])
46         var_re = re.compile(r'(?<!\sstruct )\b(%s)(?!\w)' % find)
48         found = { x: 0 for x in var_list + extern_list }
49         for var in var_re.findall(txt):
50             found[var] += 1
52         for var in sorted(var_list + extern_list):
53             if found[var] == 1:
54                 vtype = 'var' if var in var_list else 'extern'
55                 print(fn, f'has extraneous {vtype}: "{var}"')
58 def slurp_file(fn, drop_externs=False):
59     with open(fn, 'r', encoding='utf-8') as fh:
60         txt = fh.read()
61     if drop_externs:
62         txt = EXTERNS_RE.sub('', txt)
63     return txt
66 def parse_vars(fn, lines):
67     ret = [ ]
68     for line in lines:
69         line = re.sub(r'\s*\{.*\}', '', line)
70         line = re.sub(r'\s*\(.*\)', '', line)
71         for item in re.split(r'\s*,\s*', line):
72             item = re.sub(r'\s*=.*', '', item)
73             m = re.search(r'(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
74             if not m:
75                 print(f"Bogus match? ({item})")
76                 continue
77             if m['sz']:
78                 if m['var'] in sizes:
79                     if sizes[m['var']] != m['sz']:
80                         var = m['var']
81                         print(fn, f'has inconsistent size for "{var}":', m['sz'], 'vs', sizes[var])
82                 else:
83                     sizes[m['var']] = m['sz']
84             ret.append(m['var'])
85     return ret
88 if __name__ == '__main__':
89     parser = argparse.ArgumentParser(description='Check the *.c files for extraneous extern vars.', add_help=False)
90     parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
91     args = parser.parse_args()
92     main()
94 # vim: sw=4 et ft=python