Ignore svnversion.h, it's auto-generated. Ignore lemon and except.[ch], they're
[metze/wireshark/wip.git] / tools / make-dissector-reg.py
1 #!/usr/bin/env python
2 #
3 # Looks for registration routines in the protocol dissectors,
4 # and assembles C code to call all the routines.
5 #
6 # This is a Python version of the make-reg-dotc shell script.
7 # Running the shell script on Win32 is very very slow because of
8 # all the process-launching that goes on --- multiple greps and
9 # seds for each input file.  I wrote this python version so that
10 # less processes would have to be started.
11 #
12 # $Id$
13
14 import os
15 import sys
16 import re
17 import pickle
18 import hashlib
19 from stat import *
20
21 VERSION_KEY = '_VERSION'
22 CUR_VERSION = '$Id$'
23
24 #
25 # The first argument is the directory in which the source files live.
26 #
27 srcdir = sys.argv[1]
28
29 #
30 # The second argument is either "plugin" or "dissectors"; if it's
31 # "plugin", we build a plugin.c for a plugin, and if it's
32 # "dissectors", we build a register.c for libwireshark.
33 #
34 registertype = sys.argv[2]
35 if registertype == "plugin" or registertype == "plugin_wtap":
36         final_filename = "plugin.c"
37         cache_filename = None
38         preamble = """\
39 /*
40  * Do not modify this file. Changes will be overwritten.
41  *
42  * Generated automatically from %s.
43  */
44 """ % (sys.argv[0])
45 elif registertype == "dissectors":
46         final_filename = "register.c"
47         cache_filename = "register-cache.pkl"
48         preamble = """\
49 /*
50  * Do not modify this file. Changes will be overwritten.
51  *
52  * Generated automatically by the "register.c" target in
53  * epan/dissectors/Makefile or Makefile.nmake using
54  * %s
55  * and information in epan/dissectors/register-cache.pkl.
56  *
57  * You can force this file to be regenerated completely by deleting
58  * it along with epan/dissectors/register-cache.pkl.
59  */
60 """ % (sys.argv[0])
61 else:
62         print(("Unknown output type '%s'" % registertype))
63         sys.exit(1)
64
65
66 #
67 # All subsequent arguments are the files to scan.
68 #
69 files = sys.argv[3:]
70
71 # Create the proper list of filenames
72 filenames = []
73 for file in files:
74         if os.path.isfile(file):
75                 filenames.append(file)
76         else:
77                 filenames.append(os.path.join(srcdir, file))
78
79 if len(filenames) < 1:
80         print("No files found")
81         sys.exit(1)
82
83
84 # Look through all files, applying the regex to each line.
85 # If the pattern matches, save the "symbol" section to the
86 # appropriate set.
87 regs = {
88         'proto_reg': set(),
89         'handoff_reg': set(),
90         'wtap_register': set(),
91         }
92
93 # For those that don't know Python, r"" indicates a raw string,
94 # devoid of Python escapes.
95 proto_regex = r"(?P<symbol>proto_register_[_A-Za-z0-9]+)\s*\(\s*void\s*\)[^;]*$"
96
97 handoff_regex = r"(?P<symbol>proto_reg_handoff_[_A-Za-z0-9]+)\s*\(\s*void\s*\)[^;]*$"
98
99 wtap_reg_regex = r"(?P<symbol>wtap_register_[_A-Za-z0-9]+)\s*\([^;]+$"
100
101 # This table drives the pattern-matching and symbol-harvesting
102 patterns = [
103         ( 'proto_reg', re.compile(proto_regex, re.MULTILINE) ),
104         ( 'handoff_reg', re.compile(handoff_regex, re.MULTILINE) ),
105         ( 'wtap_register', re.compile(wtap_reg_regex, re.MULTILINE) ),
106         ]
107
108 # Open our registration symbol cache
109 cache = None
110 if cache_filename:
111         try:
112                 cache_file = open(cache_filename, 'rb')
113                 cache = pickle.load(cache_file)
114                 cache_file.close()
115                 if VERSION_KEY not in cache or cache[VERSION_KEY] != CUR_VERSION:
116                         cache = {VERSION_KEY: CUR_VERSION}
117         except:
118                 cache = {VERSION_KEY: CUR_VERSION}
119
120         print(("Registering %d files, %d cached" % (len(filenames), len(list(cache.keys()))-1)))
121
122 # Grep
123 cache_hits = 0
124 cache_misses = 0
125 for filename in filenames:
126         file = open(filename)
127         cur_mtime = os.fstat(file.fileno())[ST_MTIME]
128         if cache and filename in cache:
129                 cdict = cache[filename]
130                 if cur_mtime == cdict['mtime']:
131                         cache_hits += 1
132 #                       print "Pulling %s from cache" % (filename)
133                         regs['proto_reg'] |= set(cdict['proto_reg'])
134                         regs['handoff_reg'] |= set(cdict['handoff_reg'])
135                         regs['wtap_register'] |= set(cdict['wtap_register'])
136                         file.close()
137                         continue
138         # We don't have a cache entry
139         if cache is not None:
140                 cache_misses += 1
141                 cache[filename] = {
142                         'mtime': cur_mtime,
143                         'proto_reg': [],
144                         'handoff_reg': [],
145                         'wtap_register': [],
146                         }
147 #       print "Searching %s" % (filename)
148         # Read the whole file into memory
149         contents = file.read()
150         for action in patterns:
151                 regex = action[1]
152                 for match in regex.finditer(contents):
153                         symbol = match.group("symbol")
154                         sym_type = action[0]
155                         regs[sym_type].add(symbol)
156                         if cache is not None:
157 #                               print "Caching %s for %s: %s" % (sym_type, filename, symbol)
158                                 cache[filename][sym_type].append(symbol)
159         # We're done with the file contents
160         contets = ""
161         file.close()
162
163
164 if cache is not None and cache_filename is not None:
165         cache_file = open(cache_filename, 'wb')
166         pickle.dump(cache, cache_file)
167         cache_file.close()
168         print(("Cache hits: %d, misses: %d" % (cache_hits, cache_misses)))
169
170 # Make sure we actually processed something
171 if len(regs['proto_reg']) < 1:
172         print("No protocol registrations found")
173         sys.exit(1)
174
175 # Convert the sets into sorted lists to make the output pretty
176 regs['proto_reg'] = sorted(regs['proto_reg'])
177 regs['handoff_reg'] = sorted(regs['handoff_reg'])
178 regs['wtap_register'] = sorted(regs['wtap_register'])
179
180 reg_code = ""
181
182 reg_code += preamble
183
184 # Make the routine to register all protocols
185 if registertype == "plugin" or registertype == "plugin_wtap":
186         reg_code += """
187 #include "config.h"
188
189 #include <gmodule.h>
190
191 #include "moduleinfo.h"
192
193 /* plugins are DLLs */
194 #define WS_BUILD_DLL
195 #include "ws_symbol_export.h"
196
197 #ifndef ENABLE_STATIC
198 WS_DLL_PUBLIC_DEF const gchar version[] = VERSION;
199
200 /* Start the functions we need for the plugin stuff */
201
202 WS_DLL_PUBLIC_DEF void
203 plugin_register (void)
204 {
205 """
206 else:
207         reg_code += """
208 #include "register.h"
209 void
210 register_all_protocols(register_cb cb, gpointer client_data)
211 {
212 """
213
214 for symbol in regs['proto_reg']:
215         if registertype == "plugin" or registertype == "plugin_wtap":
216                 reg_code += "  {extern void %s (void); %s ();}\n" % (symbol, symbol)
217         else:
218                 reg_code += "  {extern void %s (void); if(cb) (*cb)(RA_REGISTER, \"%s\", client_data); %s ();}\n" % (symbol, symbol, symbol)
219
220 reg_code += "}\n"
221
222
223 # Make the routine to register all protocol handoffs
224 if registertype == "plugin" or registertype == "plugin_wtap":
225         reg_code += """
226 WS_DLL_PUBLIC_DEF void
227 plugin_reg_handoff(void)
228 {
229 """
230 else:
231         reg_code += """
232 void
233 register_all_protocol_handoffs(register_cb cb, gpointer client_data)
234 {
235 """
236
237 for symbol in regs['handoff_reg']:
238         if registertype == "plugin" or registertype == "plugin_wtap":
239                 reg_code += "  {extern void %s (void); %s ();}\n" % (symbol, symbol)
240         else:
241                 reg_code += "  {extern void %s (void); if(cb) (*cb)(RA_HANDOFF, \"%s\", client_data); %s ();}\n" % (symbol, symbol, symbol)
242
243 reg_code += "}\n"
244
245 if registertype == "plugin":
246         reg_code += "#endif\n"
247 elif registertype == "plugin_wtap":
248         reg_code += """
249 WS_DLL_PUBLIC_DEF void
250 register_wtap_module(void)
251 {
252 """
253
254         for symbol in regs['wtap_register']:
255                 line = "  {extern void %s (void); %s ();}\n" % (symbol, symbol)
256                 reg_code += line
257
258         reg_code += """
259 }
260 #endif
261 """
262
263 else:
264         reg_code += """
265 static gulong proto_reg_count(void)
266 {
267   return %(proto_reg_len)d;
268 }
269
270 static gulong handoff_reg_count(void)
271 {
272   return %(handoff_reg_len)d;
273 }
274
275 gulong register_count(void)
276 {
277   return proto_reg_count() + handoff_reg_count();
278 }
279
280 """ % {
281         'proto_reg_len': len(regs['proto_reg']),
282         'handoff_reg_len': len(regs['handoff_reg'])
283       }
284
285
286 # Compare current and new content and update the file if anything has changed.
287
288 try:    # Python >= 2.6, >= 3.0
289         reg_code_bytes = bytes(reg_code.encode('utf-8'))
290 except:
291         reg_code_bytes = reg_code
292
293 new_hash = hashlib.sha1(reg_code_bytes).hexdigest()
294
295 try:
296         fh = open(final_filename, 'rb')
297         cur_hash = hashlib.sha1(fh.read()).hexdigest()
298         fh.close()
299 except:
300         cur_hash = ''
301
302 try:
303         if new_hash != cur_hash:
304                 print(('Updating ' + final_filename))
305                 fh = open(final_filename, 'w')
306                 fh.write(reg_code)
307                 fh.close()
308         else:
309                 print((final_filename + ' unchanged.'))
310                 os.utime(final_filename, None)
311 except OSError:
312         sys.exit('Unable to write ' + final_filename + '.\n')
313
314 #
315 # Editor modelines  -  http://www.wireshark.org/tools/modelines.html
316 #
317 # Local variables:
318 # c-basic-offset: 8
319 # tab-width: 8
320 # indent-tabs-mode: t
321 # End:
322 #
323 # vi: set shiftwidth=8 tabstop=8 noexpandtab:
324 # :indentSize=8:tabSize=8:noTabs=false:
325 #