Introduce PTR_SUB
[rsync.git] / packaging / var-checker
blob1573895c0511256014106c1c8ce94654038764b6
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][^ \n\t:]*\s+.*);', re.M)
10 EXTERNS_RE = re.compile(r'^extern\s+(.*);', re.M)
12 types = { }
13 sizes = { }
15 def main():
16     add_syscall_c = set('t_stub.c t_unsafe.c tls.c trimslash.c'.split())
17     add_util_c = set('t_stub.c t_unsafe.c'.split())
19     if not os.path.exists('syscall.c'):
20         if os.path.exists('var-checker'):
21             os.chdir('..')
22         else:
23             print("Couldn't find the source dir.")
24             sys.exit(1)
26     syscall_c = slurp_file('syscall.c', True)
27     util_c = slurp_file('util1.c', True)
29     for fn in sorted(glob.glob('*.c')):
30         txt = slurp_file(fn)
32         var_list = parse_vars(fn, VARS_RE.findall(txt))
33         extern_list = parse_vars(fn, EXTERNS_RE.findall(txt))
34         if not var_list and not extern_list:
35             continue
37         if fn in add_syscall_c:
38             txt += syscall_c
39         if fn in add_util_c:
40             txt += util_c
42         txt = re.sub(r'INFO_GTE', 'info_levels ', txt)
43         txt = re.sub(r'DEBUG_GTE', 'debug_levels ', txt)
44         txt = re.sub(r'SIGACTION\(', 'sigact (', txt)
46         find = '|'.join([ re.escape(x) for x in var_list + extern_list ])
47         var_re = re.compile(r'(?<!\sstruct )\b(%s)(?!\w)' % find)
49         found = { x: 0 for x in var_list + extern_list }
50         for var in var_re.findall(txt):
51             found[var] += 1
53         for var in sorted(var_list + extern_list):
54             if found[var] == 1:
55                 vtype = 'var' if var in var_list else 'extern'
56                 print(fn, f'has extraneous {vtype}: "{var}"')
59 def slurp_file(fn, drop_externs=False):
60     with open(fn, 'r', encoding='utf-8') as fh:
61         txt = fh.read()
62     if drop_externs:
63         txt = EXTERNS_RE.sub('', txt)
64     return txt
67 def parse_vars(fn, lines):
68     ret = [ ]
69     for line in lines:
70         line = re.sub(r'\s*\{.*\}', '', line)
71         line = re.sub(r'\s*\(.*\)', '', line)
72         line = re.sub(r'\s*=\s*[^,]*', '', line)
73         m = re.search(r'^(?:(?:static|extern)\s+)?(?P<type>[^\[,]+?)(?P<vars>\w+([\[,].+)?)$', line)
74         if not m:
75             print(f"Bogus match? ({line})")
76             continue
77         items = m['vars']
78         main_type = m['type'].strip()
79         mt_len = len(main_type)
80         main_type = main_type.rstrip('*')
81         first_stars = '*' * (mt_len - len(main_type))
82         if first_stars:
83             main_type = main_type.rstrip()
84             items = first_stars + items
85         for item in re.split(r'\s*,\s*', items):
86             m = re.search(r'(?P<stars>\*+\s*)?(?P<var>\w+)(?P<sz>\[.*?\])?$', item)
87             if not m:
88                 print(f"Bogus match? ({item})")
89                 continue
90             typ = main_type
91             if m['stars']:
92                 typ = typ + m['stars'].strip()
93             chk = [
94                     'type', typ, types,
95                     'size', m['sz'], sizes,
96                     ]
97             while chk:
98                 label = chk.pop(0)
99                 new = chk.pop(0)
100                 lst = chk.pop(0)
101                 if label == 'type':
102                     new = ' '.join(new.split()).replace(' *', '*')
103                 if m['var'] in lst:
104                     old = lst[m['var']]
105                     if new != old:
106                         var = m['var']
107                         print(fn, f'has inconsistent {label} for "{var}":', new, 'vs', old)
108                 else:
109                     lst[m['var']] = new
110             ret.append(m['var'])
111     return ret
114 if __name__ == '__main__':
115     parser = argparse.ArgumentParser(description='Check the *.c files for extraneous extern vars.', add_help=False)
116     parser.add_argument("--help", "-h", action="help", help="Output this help message and exit.")
117     args = parser.parse_args()
118     main()
120 # vim: sw=4 et ft=python