fix up contrib/checkOptionDocs.pl to match current practice (asciidoc manpage, no...
[tor.git] / contrib / mdd.py
blobe0b496b8fe2a5788996863e6420c2bbb0c654ffc
1 #!/usr/bin/env python2.3
3 import re, sys
4 import textwrap
6 files = sys.argv[1:]
7 funcDeclaredIn = {}
8 fileDeclares = {}
9 functionCalls = {}
10 funcCalledByFile = {}
11 funcCalledByFunc = {}
13 cpp_re = re.compile(r'//.*$')
14 c_re = re.compile(r'/[*]+(?:[^*]+|[*]+[^/*])*[*]+/', re.M|re.S)
16 for fname in files:
17 f = open(fname, 'r')
18 curFunc = "???"
19 functionCalls.setdefault(curFunc,{})
20 lineno = 0
21 body = f.read()
22 body = cpp_re.sub(" ",body)
23 body = c_re.sub(" ",body)
24 #if fname == 'dns.c': print body
25 for line in body.split("\n"):
26 lineno += 1
27 m = re.match(r'^[^\s/].*\s(\w+)\([^;]*$', line)
28 if m:
29 #print line, "->", m.group(1)
30 curFunc = m.group(1)
31 if curFunc[0] == '_': curFunc = curFunc[1:]
32 functionCalls.setdefault(curFunc,{})
33 funcDeclaredIn[m.group(1)] = fname
34 fileDeclares.setdefault(fname, {})[m.group(1)] = 1
35 continue
36 m = re.match(r'^(\w+)\([^;]', line)
37 if m:
38 #print line, "->", m.group(1)
39 curFunc = m.group(1)
40 if curFunc[0] == '_': curFunc = curFunc[1:]
41 functionCalls.setdefault(curFunc,{})
42 funcDeclaredIn[m.group(1)] = fname
43 fileDeclares.setdefault(fname, {})[m.group(1)] = 1
44 continue
45 while line:
46 m = re.search(r'(\w+)\(', line)
47 if not m: break
48 #print fname, line, curFunc, "->", m.group(1)
49 fn = m.group(1)
50 if fn[0] == '_':
51 fn = fn[1:]
52 functionCalls[curFunc][m.group(1)] = 1
53 #if curFunc == "???":
54 # print ">>!!!!! at %s:%s"%(fname,lineno)
55 funcCalledByFunc.setdefault(m.group(1), {})[curFunc]=1
56 funcCalledByFile.setdefault(m.group(1), {})[fname]=1
57 line = line[m.end():]
59 f.close()
61 fileUsers = {}
62 fileUses = {}
64 for fname in files:
65 print "%s:"%fname
66 users = {}
67 for func in fileDeclares[fname]:
68 cb = funcCalledByFile.get(func,{}).keys()
69 for f in cb: users[f] = 1
70 #print "users[%s] = %s"%(f,users[f])
71 users = users.keys()
72 users.sort()
73 fileUsers[fname] = users
74 for user in users:
75 fileUses.setdefault(user,[]).append(fname)
76 if user == fname: continue
77 print " from %s:"%user
78 for func in fileDeclares[fname]:
79 if funcCalledByFile.get(func,{}).get(user,0):
80 print " %s()"%func
82 def wrap(s, pre):
83 return textwrap.fill(s,
84 width=77, initial_indent=pre,
85 subsequent_indent=" "*len(pre))
87 for fname in files:
88 print
89 print "===== %s"%fname
90 print wrap(" ".join(fileUses[fname]),
91 " Calls: ")
92 print wrap(" ".join(fileUsers[fname]),
93 " Called by: ")
95 print "=============================="
97 funcnames = functionCalls.keys()
98 funcnames.sort()
100 if 1:
101 for func in funcnames:
102 print "===== %s"%func
103 callers = [c for c in funcCalledByFunc.get(func,{}).keys()
104 if c != "???"]
105 callers.sort()
106 called = [c for c in functionCalls[func].keys() if c != "???" and
107 c in funcnames]
108 called.sort()
109 print wrap(" ".join(callers),
110 " Called by:")
111 print wrap(" ".join(called),
112 " Calls:")
114 # simple topological sort.
115 functionDepth = {}
116 while 1:
117 BIG = 1000000
118 any = 0
119 for func in funcnames:
120 if functionDepth.has_key(func):
121 continue
122 called = [c for c in functionCalls[func] if c != func and
123 functionCalls.has_key(c)]
124 if len(called) == 0:
125 functionDepth[func] = 0
126 #print "Depth(%s)=%s"%(func,0)
127 any = 1
128 continue
129 calledDepths = [ functionDepth.get(c,BIG) for c in called ]
130 if max(calledDepths) < BIG:
131 d = functionDepth[func] = max(calledDepths)+1
132 #print "Depth(%s)=%s"%(func,d)
133 any = 1
134 continue
135 if not any:
136 break
138 # compute lexical closure.
139 cycCalls = {}
140 for func in funcnames:
141 if not functionDepth.has_key(func):
142 calls = [ c for c in functionCalls[func] if c != func and
143 functionCalls.has_key(c) and not functionDepth.has_key(c)]
144 cycCalls[func] = d = {}
145 for c in calls:
146 d[c]=1
148 cycNames = cycCalls.keys()
149 while 1:
150 any = 0
151 for func in cycNames:
152 L = len(cycCalls[func])
153 for called in cycCalls[func].keys():
154 cycCalls[func].update(cycCalls[called])
155 if L != len(cycCalls[func]):
156 any = 1
157 if not any:
158 break
160 depthList = [ (v,k) for k,v in functionDepth.items() ]
161 depthList.sort()
162 cycList = [ (len(v),k) for k,v in cycCalls.items() ]
163 cycList.sort()
164 for depth,name in depthList:
165 print "Depth[%s]=%s"%(name,depth)
166 for bredth,name in cycList:
167 print "Width[%s]=%s"%(name,bredth)
169 print "Sorted %s / %s"%(len(functionDepth),len(funcnames))