]> git.proxmox.com Git - ovs.git/blob - build-aux/extract-ofp-errors
ofp-errors: Ignore text enclosed in square brackets
[ovs.git] / build-aux / extract-ofp-errors
1 #! /usr/bin/python
2
3 import sys
4 import os.path
5 import re
6
7 macros = {}
8
9 token = None
10 line = ""
11 idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
12 tokenRe = "#?" + idRe + "|[0-9]+|."
13 inComment = False
14 inDirective = False
15
16 def getLine():
17 global line
18 global lineNumber
19 line = inputFile.readline()
20 lineNumber += 1
21 if line == "":
22 fatal("unexpected end of input")
23
24 def getToken():
25 global token
26 global line
27 global inComment
28 global inDirective
29 while True:
30 line = line.lstrip()
31 if line != "":
32 if line.startswith("/*"):
33 inComment = True
34 line = line[2:]
35 elif inComment:
36 commentEnd = line.find("*/")
37 if commentEnd < 0:
38 line = ""
39 else:
40 inComment = False
41 line = line[commentEnd + 2:]
42 else:
43 match = re.match(tokenRe, line)
44 token = match.group(0)
45 line = line[len(token):]
46 if token.startswith('#'):
47 inDirective = True
48 elif token in macros and not inDirective:
49 line = macros[token] + line
50 continue
51 return True
52 elif inDirective:
53 token = "$"
54 inDirective = False
55 return True
56 else:
57 global lineNumber
58 line = inputFile.readline()
59 lineNumber += 1
60 while line.endswith("\\\n"):
61 line = line[:-2] + inputFile.readline()
62 lineNumber += 1
63 if line == "":
64 if token == None:
65 fatal("unexpected end of input")
66 token = None
67 return False
68
69 n_errors = 0
70 def error(msg):
71 global n_errors
72 sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
73 n_errors += 1
74
75 def fatal(msg):
76 error(msg)
77 sys.exit(1)
78
79 def skipDirective():
80 getToken()
81 while token != '$':
82 getToken()
83
84 def isId(s):
85 return re.match(idRe + "$", s) != None
86
87 def forceId():
88 if not isId(token):
89 fatal("identifier expected")
90
91 def forceInteger():
92 if not re.match('[0-9]+$', token):
93 fatal("integer expected")
94
95 def match(t):
96 if token == t:
97 getToken()
98 return True
99 else:
100 return False
101
102 def forceMatch(t):
103 if not match(t):
104 fatal("%s expected" % t)
105
106 def parseTaggedName():
107 assert token in ('struct', 'union')
108 name = token
109 getToken()
110 forceId()
111 name = "%s %s" % (name, token)
112 getToken()
113 return name
114
115 def print_enum(tag, constants, storage_class):
116 print """
117 %(storage_class)sconst char *
118 %(tag)s_to_string(uint16_t value)
119 {
120 switch (value) {\
121 """ % {"tag": tag,
122 "bufferlen": len(tag) + 32,
123 "storage_class": storage_class}
124 for constant in constants:
125 print " case %s: return \"%s\";" % (constant, constant)
126 print """\
127 }
128 return NULL;
129 }\
130 """ % {"tag": tag}
131
132 def usage():
133 argv0 = os.path.basename(sys.argv[0])
134 print '''\
135 %(argv0)s, for extracting OpenFlow error codes from header files
136 usage: %(argv0)s FILE [FILE...]
137
138 This program reads the header files specified on the command line and
139 outputs a C source file for translating OpenFlow error codes into
140 strings, for use as lib/ofp-errors.c in the Open vSwitch source tree.
141
142 This program is specialized for reading lib/ofp-errors.h. It will not
143 work on arbitrary header files without extensions.\
144 ''' % {"argv0": argv0}
145 sys.exit(0)
146
147 def extract_ofp_errors(filenames):
148 error_types = {}
149
150 comments = []
151 names = []
152 domain = {}
153 reverse = {}
154 for domain_name in ("OF1.0", "OF1.1", "OF1.2", "NX1.0", "NX1.1"):
155 domain[domain_name] = {}
156 reverse[domain_name] = {}
157
158 n_errors = 0
159 expected_errors = {}
160
161 global fileName
162 for fileName in filenames:
163 global inputFile
164 global lineNumber
165 inputFile = open(fileName)
166 lineNumber = 0
167
168 while True:
169 getLine()
170 if re.match('enum ofperr', line):
171 break
172
173 while True:
174 getLine()
175 if line.startswith('/*') or not line or line.isspace():
176 continue
177 elif re.match('}', line):
178 break
179
180 if not line.lstrip().startswith('/*'):
181 fatal("unexpected syntax between errors")
182
183 comment = line.lstrip()[2:].strip()
184 while not comment.endswith('*/'):
185 getLine()
186 if line.startswith('/*') or not line or line.isspace():
187 fatal("unexpected syntax within error")
188 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
189 comment = comment[:-2].rstrip()
190
191 m = re.match('Expected: (.*)\.$', comment)
192 if m:
193 expected_errors[m.group(1)] = (fileName, lineNumber)
194 continue
195
196 m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
197 if not m:
198 fatal("unexpected syntax between errors")
199
200 dsts, comment = m.groups()
201
202 getLine()
203 m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
204 line)
205 if not m:
206 fatal("syntax error expecting enum value")
207
208 enum = m.group(1)
209
210 comments.append(re.sub('\[[^]]*\]', '', comment))
211 names.append(enum)
212
213 for dst in dsts.split(', '):
214 m = re.match(r'([A-Z0-9.+]+)\((\d+|(0x)[0-9a-fA-F]+)(?:,(\d+))?\)$', dst)
215 if not m:
216 fatal("%s: syntax error in destination" % dst)
217 targets = m.group(1)
218 if m.group(3):
219 base = 16
220 else:
221 base = 10
222 type_ = int(m.group(2), base)
223 if m.group(4):
224 code = int(m.group(4))
225 else:
226 code = None
227
228 target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2"),
229 "OF1.1+": ("OF1.1", "OF1.2"),
230 "OF1.2+": ("OF1.2",),
231 "OF1.0": ("OF1.0",),
232 "OF1.1": ("OF1.1",),
233 "OF1.2": ("OF1.2",),
234 "NX1.0+": ("OF1.0", "OF1.1", "OF1.2"),
235 "NX1.0": ("OF1.0",),
236 "NX1.1": ("OF1.1",),
237 "NX1.1+": ("OF1.1",),
238 "NX1.2": ("OF1.2",)}
239 if targets not in target_map:
240 fatal("%s: unknown error domain" % targets)
241 for target in target_map[targets]:
242 domain[target].setdefault(type_, {})
243 if code in domain[target][type_]:
244 msg = "%d,%d in %s means both %s and %s" % (
245 type_, code, target,
246 domain[target][type_][code][0], enum)
247 if msg in expected_errors:
248 del expected_errors[msg]
249 else:
250 error("%s: %s." % (dst, msg))
251 sys.stderr.write("%s:%d: %s: Here is the location "
252 "of the previous definition.\n"
253 % (domain[target][type_][code][1],
254 domain[target][type_][code][2],
255 dst))
256 else:
257 domain[target][type_][code] = (enum, fileName,
258 lineNumber)
259
260 if enum in reverse[target]:
261 error("%s: %s in %s means both %d,%d and %d,%d." %
262 (dst, enum, target,
263 reverse[target][enum][0],
264 reverse[target][enum][1],
265 type_, code))
266 reverse[target][enum] = (type_, code)
267
268 inputFile.close()
269
270 for fn, ln in expected_errors.itervalues():
271 sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
272 n_errors += 1
273
274 if n_errors:
275 sys.exit(1)
276
277 print """\
278 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
279
280 #define OFPERR_N_ERRORS %d
281
282 struct ofperr_domain {
283 const char *name;
284 uint8_t version;
285 enum ofperr (*decode)(uint16_t type, uint16_t code);
286 enum ofperr (*decode_type)(uint16_t type);
287 struct pair errors[OFPERR_N_ERRORS];
288 };
289
290 static const char *error_names[OFPERR_N_ERRORS] = {
291 %s
292 };
293
294 static const char *error_comments[OFPERR_N_ERRORS] = {
295 %s
296 };\
297 """ % (len(names),
298 '\n'.join(' "%s",' % name for name in names),
299 '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
300 for comment in comments))
301
302 def output_domain(map, name, description, version):
303 print """
304 static enum ofperr
305 %s_decode(uint16_t type, uint16_t code)
306 {
307 switch ((type << 16) | code) {""" % name
308 found = set()
309 for enum in names:
310 if enum not in map:
311 continue
312 type_, code = map[enum]
313 if code is None:
314 continue
315 value = (type_ << 16) | code
316 if value in found:
317 continue
318 found.add(value)
319 print " case (%d << 16) | %d:" % (type_, code)
320 print " return OFPERR_%s;" % enum
321 print """\
322 }
323
324 return 0;
325 }
326
327 static enum ofperr
328 %s_decode_type(uint16_t type)
329 {
330 switch (type) {""" % name
331 for enum in names:
332 if enum not in map:
333 continue
334 type_, code = map[enum]
335 if code is not None:
336 continue
337 print " case %d:" % type_
338 print " return OFPERR_%s;" % enum
339 print """\
340 }
341
342 return 0;
343 }"""
344
345 print """
346 static const struct ofperr_domain %s = {
347 "%s",
348 %d,
349 %s_decode,
350 %s_decode_type,
351 {""" % (name, description, version, name, name)
352 for enum in names:
353 if enum in map:
354 type_, code = map[enum]
355 if code == None:
356 code = -1
357 else:
358 type_ = code = -1
359 print " { %2d, %3d }, /* %s */" % (type_, code, enum)
360 print """\
361 },
362 };"""
363
364 output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
365 output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
366 output_domain(reverse["OF1.2"], "ofperr_of12", "OpenFlow 1.2", 0x03)
367
368 if __name__ == '__main__':
369 if '--help' in sys.argv:
370 usage()
371 elif len(sys.argv) < 2:
372 sys.stderr.write("at least one non-option argument required; "
373 "use --help for help\n")
374 sys.exit(1)
375 else:
376 extract_ofp_errors(sys.argv[1:])