11 idRe = "[a-zA-Z_][a-zA-Z_0-9]*"
12 tokenRe = "#?" + idRe + "|[0-9]+|."
19 line = inputFile.readline()
22 fatal("unexpected end of input")
32 if line.startswith("/*"):
36 commentEnd = line.find("*/")
41 line = line[commentEnd + 2:]
43 match = re.match(tokenRe, line)
44 token = match.group(0)
45 line = line[len(token):]
46 if token.startswith('#'):
48 elif token in macros and not inDirective:
49 line = macros[token] + line
58 line = inputFile.readline()
60 while line.endswith("\\\n"):
61 line = line[:-2] + inputFile.readline()
65 fatal("unexpected end of input")
72 sys.stderr.write("%s:%d: %s\n" % (fileName, lineNumber, msg))
85 return re.match(idRe + "$", s) != None
89 fatal("identifier expected")
92 if not re.match('[0-9]+$', token):
93 fatal("integer expected")
104 fatal("%s expected" % t)
106 def parseTaggedName():
107 assert token in ('struct', 'union')
111 name = "%s %s" % (name, token)
115 def print_enum(tag, constants, storage_class):
117 %(storage_class)sconst char *
118 %(tag)s_to_string(uint16_t value)
122 "bufferlen": len(tag) + 32,
123 "storage_class": storage_class}
124 for constant in constants:
125 print " case %s: return \"%s\";" % (constant, constant)
133 argv0 = os.path.basename(sys.argv[0])
135 %(argv0)s, for extracting OpenFlow error codes from header files
136 usage: %(argv0)s FILE [FILE...]
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.
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}
147 def extract_ofp_errors(filenames):
154 for domain_name in ("OF1.0", "OF1.1", "OF1.2", "OF1.3",
155 "NX1.0", "NX1.1", "NX1.2", "NX1.3"):
156 domain[domain_name] = {}
157 reverse[domain_name] = {}
163 for fileName in filenames:
166 inputFile = open(fileName)
171 if re.match('enum ofperr', line):
176 if line.startswith('/*') or not line or line.isspace():
178 elif re.match('}', line):
181 if not line.lstrip().startswith('/*'):
182 fatal("unexpected syntax between errors")
184 comment = line.lstrip()[2:].strip()
185 while not comment.endswith('*/'):
187 if line.startswith('/*') or not line or line.isspace():
188 fatal("unexpected syntax within error")
189 comment += ' %s' % line.lstrip('* \t').rstrip(' \t\r\n')
190 comment = comment[:-2].rstrip()
192 m = re.match('Expected: (.*)\.$', comment)
194 expected_errors[m.group(1)] = (fileName, lineNumber)
197 m = re.match('((?:.(?!\. ))+.)\. (.*)$', comment)
199 fatal("unexpected syntax between errors")
201 dsts, comment = m.groups()
204 m = re.match('\s+(?:OFPERR_((?:OFP|NX)[A-Z0-9_]+))(\s*=\s*OFPERR_OFS)?,',
207 fatal("syntax error expecting enum value")
211 comments.append(re.sub('\[[^]]*\]', '', comment))
214 for dst in dsts.split(', '):
215 m = re.match(r'([A-Z0-9.+]+)\((\d+|(0x)[0-9a-fA-F]+)(?:,(\d+))?\)$', dst)
217 fatal("%s: syntax error in destination" % dst)
223 type_ = int(m.group(2), base)
225 code = int(m.group(4))
229 target_map = {"OF1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
230 "OF1.1+": ("OF1.1", "OF1.2", "OF1.3"),
231 "OF1.2+": ("OF1.2", "OF1.3"),
232 "OF1.3+": ("OF1.3",),
237 "NX1.0+": ("OF1.0", "OF1.1", "OF1.2", "OF1.3"),
238 "NX1.1+": ("OF1.1", "OF1.2", "OF1.3"),
239 "NX1.2+": ("OF1.2", "OF1.3"),
240 "NX1.3+": ("OF1.3",),
245 if targets not in target_map:
246 fatal("%s: unknown error domain" % targets)
247 if targets.startswith('NX') and code < 0x100:
248 fatal("%s: NX domain code cannot be less than 0x100" % dst)
249 if targets.startswith('OF') and code >= 0x100:
250 fatal("%s: OF domain code cannot be greater than 0x100"
252 for target in target_map[targets]:
253 domain[target].setdefault(type_, {})
254 if code in domain[target][type_]:
255 msg = "%d,%d in %s means both %s and %s" % (
257 domain[target][type_][code][0], enum)
258 if msg in expected_errors:
259 del expected_errors[msg]
261 error("%s: %s." % (dst, msg))
262 sys.stderr.write("%s:%d: %s: Here is the location "
263 "of the previous definition.\n"
264 % (domain[target][type_][code][1],
265 domain[target][type_][code][2],
268 domain[target][type_][code] = (enum, fileName,
271 if enum in reverse[target]:
272 error("%s: %s in %s means both %d,%d and %d,%d." %
274 reverse[target][enum][0],
275 reverse[target][enum][1],
277 reverse[target][enum] = (type_, code)
281 for fn, ln in expected_errors.itervalues():
282 sys.stderr.write("%s:%d: expected duplicate not used.\n" % (fn, ln))
289 /* Generated automatically; do not modify! -*- buffer-read-only: t -*- */
291 #define OFPERR_N_ERRORS %d
293 struct ofperr_domain {
296 enum ofperr (*decode)(uint16_t type, uint16_t code);
297 struct pair errors[OFPERR_N_ERRORS];
300 static const char *error_names[OFPERR_N_ERRORS] = {
304 static const char *error_comments[OFPERR_N_ERRORS] = {
308 '\n'.join(' "%s",' % name for name in names),
309 '\n'.join(' "%s",' % re.sub(r'(["\\])', r'\\\1', comment)
310 for comment in comments))
312 def output_domain(map, name, description, version):
315 %s_decode(uint16_t type, uint16_t code)
317 switch ((type << 16) | code) {""" % name
322 type_, code = map[enum]
325 value = (type_ << 16) | code
329 print " case (%d << 16) | %d:" % (type_, code)
330 print " return OFPERR_%s;" % enum
338 static const struct ofperr_domain %s = {
342 {""" % (name, description, version, name)
345 type_, code = map[enum]
350 print " { %2d, %3d }, /* %s */" % (type_, code, enum)
355 output_domain(reverse["OF1.0"], "ofperr_of10", "OpenFlow 1.0", 0x01)
356 output_domain(reverse["OF1.1"], "ofperr_of11", "OpenFlow 1.1", 0x02)
357 output_domain(reverse["OF1.2"], "ofperr_of12", "OpenFlow 1.2", 0x03)
358 output_domain(reverse["OF1.3"], "ofperr_of13", "OpenFlow 1.3", 0x04)
360 if __name__ == '__main__':
361 if '--help' in sys.argv:
363 elif len(sys.argv) < 2:
364 sys.stderr.write("at least one non-option argument required; "
365 "use --help for help\n")
368 extract_ofp_errors(sys.argv[1:])