3 import sys, re, signal, tempfile, os, os.path, shutil, atexit
5 basedir=os.path.abspath(os.path.split(sys.argv[0])[0])
9 TEX_HEADER = r"""\documentclass{scrartcl}
10 \usepackage[german]{babel}
11 \usepackage[latin1]{inputenc}
12 \usepackage[T1]{fontenc}
13 \usepackage{ae,aecompl}
14 \usepackage[active]{srcltx}
17 \usepackage{bytefield}
25 PACKET_HEADER=r"""\begin{bytefield}{32}
29 PACKET_FOOTER=r"""\end{bytefield}
34 TEX_FOOTER = r"""\end{document}
37 def formatField(width, start, size):
42 areas.append({'start': start,
46 areas.append({'start': start,
51 for i in range(len(areas)-1):
52 if areas[i]['start'] < areas[i+1]['start']+areas[i+1]['size']:
53 areas[i]['bottom'] = False
54 areas[i+1]['top'] = False
57 def formatSimpleField(width, start, field):
58 areas = formatField(width, start, field['size'])
61 for i in range(len(areas)):
62 if areas[i]['size'] > namesz:
63 namesz = areas[i]['size']
65 areas[nameix]['name'] = field['name'][:int(areas[nameix]['size'] * charsPerBit)]
66 if len(areas) == 2 and areas[0].get('bottom',True):
67 if areas[0].get('name','') : ix = 1
69 if 6 <= int(areas[ix]['size'] * charsPerBit):
70 areas[ix]['name'] = '(cont)'
73 def formatPacket(width, fields):
78 if field.get('repeat', False):
79 if start > 0 and start < width:
80 areas.append({ 'start': start, 'size': width-start, 'bottom': False,
83 if field.get('size',None):
84 areas.extend(formatSimpleField(width, start, field))
85 start = areas[-1]['start'] + areas[-1]['size']
86 elif field.get('minsize', None):
88 f['size'] = field['minsize']
89 areas.extend(formatSimpleField(width, start, f))
90 start = areas[-1]['start'] + areas[-1]['size']
91 if start >= width : start = 0
92 addareas = formatField(width, start, field['maxsize'] - field['minsize'])
96 start = areas[-1]['start'] + areas[-1]['size']
97 if start > 0 and start < width:
98 areas.append({ 'start': start, 'size': width-start, 'bottom': False,
102 if start > 0 and start < width:
103 areas.append({ 'start': start, 'size': width-start, 'bottom': False,
105 areas.extend([ { 'start': 0, 'size': width, 'bottom': False,
106 'name': field['name'] },
107 { 'start': 0, 'size': width, 'skip': True },
108 { 'start': 0, 'size': width, 'top': False } ])
110 if field.get('repeat'):
111 if start > 0 and start < width:
112 areas.append({ 'start': start, 'size': width-start, 'bottom': False,
115 areas.append({ 'start': 0, 'size': width, 'dots': True })
116 da = areas[(areas[0].get('right', True) and (0,) or (1,))[0]:-1]
117 for i in range(len(da)):
118 if da[i].get('name','') :
122 if start == width : start = 0
125 while areas and (not(rows[-1]) or rows[-1][-1]['start'] + rows[-1][-1]['size'] < width):
126 if areas[0].get('right', True) == False:
127 # This is a fillup field. Check, wether to draw top:
129 areas[0]['top'] = False
130 elif rows[-2][-1].get('bottom', True):
131 areas[0]['top'] = False
132 rows[-1].append(areas.pop(0))
138 s = s.replace('_', '\\_')
147 if area.get('left', True) : sides += "l"
148 if area.get('right', True) : sides += "r"
149 if area.get('top', True) : sides += "t"
150 if area.get('bottom', True) : sides += "b"
151 if sides == "lrtb" : sides = ""
152 else : sides = "[%s]" % sides
153 if area.get('filled', False):
154 line.append(r"\bitbox%s{%s}{\color[gray]{0.7}\rule{\width}{\height}}" % (sides, area['size']))
155 elif area.get('skip', False):
156 line.append(r"\skippedwords")
157 elif area.get('dots', False):
158 line.append(r"\wordbox[]{1}{$\vdots$\\[1ex]}")
160 line.append(r"\bitbox%s{%s}{\strut %s}"
161 % (sides, area['size'], texquote(area.get('name',''))))
162 lines.append(" & ".join(line))
163 return " \\\\\n".join(lines) + "\n"
166 'UInt8Parser' : {'size': 8 },
167 'UInt16Parser' : {'size': 16 },
168 'UInt24Parser' : {'size': 24 },
169 'UInt32Parser' : {'size': 32 },
170 'UInt64Parser' : {'size': 64 },
171 'Int8Parser' : {'size': 8 },
172 'Int16Parser' : {'size': 16 },
173 'Int24Parser' : {'size': 24 },
174 'Int32Parser' : {'size': 32 },
175 'Int64Parser' : {'size': 64 },
176 'MACAddressParser': {'size': 48 },
177 'INet4AddressParser' : {'size': 32 },
178 'INet6AddressParser' : {'size': 128 },
181 def parse_FIELD(args, flags):
182 args = [ arg.strip() for arg in args.split(',') ]
184 sys.stderr.write("Failed to parse FIELD: %s" % args)
186 field = dict(FIELD_TYPES.get(args[1].split(':')[-1], {}))
187 field['name'] = args[0]
190 def parse_PRIVATE_FIELD(args, flags):
191 return parse_FIELD(args, flags)
193 def parse_FIELD_RO(args, flags):
194 return parse_FIELD(args, flags)
196 def parse_BITFIELD(args, flags):
197 args = [ arg.strip() for arg in args.split(',') ]
199 sys.stderr.write("Failed to parse BITFIELD: %s" % args)
204 sys.stderr.write("Failed to parse BITFIELD: %s" % args)
206 return { 'size' : size, 'name' : args[0] }
208 def parse_PRIVATE_BITFIELD(args, flags):
209 return parse_BITFIELD(args, flags)
211 def parse_BITFIELD_RO(args, flags):
212 return parse_BITFIELD(args, flags)
214 def parse_SKIP(args, flags):
215 args = args.split(',')[0]
217 bytes = int(args.strip())
219 sys.stderr.write("Failed to parse SKIP: %s" % args)
221 return { 'size': 8*bytes, 'name': '' }
223 def parse_SKIP_BITS(args, flags):
225 bits = int(args.strip())
227 sys.stderr.write("Failed to parse SKIP_BITS: %s" % args)
229 return { 'size': bits, 'name': '' }
231 def parse_VECTOR(args, flags):
232 args = [ arg.strip() for arg in args.split(',') ]
234 sys.stderr.write("Failed to aprse VECTOR: %s" % args)
236 field = dict(FIELD_TYPES.get(args[-1].split(':')[-1], {}))
237 field['name'] = args[0]
238 field['repeat'] = True
241 def parse_LIST(args, flags):
242 return parse_VECTOR(args, flags)
244 def parse_VARIANT(args, flags):
245 return { 'name': args.split(',',1)[0].strip() }
247 def parse_INIT(args, flags):
250 PARSER_START_RE = re.compile(r"#\s*include\s+SENF_(FIXED_)?PARSER\s*\(\s*\)")
251 PARSER_END_RE = re.compile(r"SENF_PARSER_FINALIZE\s*\(([^)]*)\)\s*;")
252 PARSER_FIELD_RE = re.compile(r"(?://>pkgdraw:(.*)$\s*)?SENF_PARSER_([A-Z_]+)\s*\(([^;]*)\)\s*;(?:\s*//<pkgdraw:(.*)$)?", re.M)
254 def scanPackets(data):
258 start = PARSER_START_RE.search(data, end)
259 if not start: return packets
261 end = PARSER_END_RE.search(data, start)
262 if not end: return packets
263 name=end.group(1).strip()
265 packets[name] = scanFields(data[start:end])
267 def scanFields(data):
269 for match in PARSER_FIELD_RE.finditer(data):
271 flags = dict([ ([ arg.strip() for arg in flag.strip().split('=',1) ]+[True])[:2]
272 for flag in ((match.group(1) or '')+(match.group(4) or '')).split(',') ])
273 if flags.has_key('hide') : continue
274 parser = globals().get("parse_%s" % tp, None)
276 field = parser(match.group(3).strip(), flags)
278 if flags.has_key('name') : field['name'] = flags['name']
279 field['name'] = field['name'].strip('_')
282 sys.stderr.write("Unknown parser type: %s\n" % tp)
285 tmpdir = tempfile.mkdtemp(prefix="pkgdraw_")
289 shutil.rmtree(tmpdir)
291 signal.signal(signal.SIGINT, cleanup)
292 signal.signal(signal.SIGTERM, cleanup)
293 signal.signal(signal.SIGHUP, cleanup)
294 #atexit.register(cleanup)
296 data = scanPackets(sys.stdin.read())
298 texf = file(os.path.join(tmpdir, "fields.tex"),"w")
299 texf.write(TEX_HEADER)
301 if len(sys.argv) > 1:
308 texf.write("\\textbf{%s}\n\\bigskip\n\n" % texquote(name))
309 texf.write(PACKET_HEADER)
310 texf.write(makeTex(formatPacket(32, data[name])))
311 texf.write(PACKET_FOOTER)
313 texf.write(TEX_FOOTER)
316 if os.system("cd %s; %s/textogif -png -dpi 80 -res 0.25 fields >pkgdraw.log 2>&1"
317 % (tmpdir, basedir)) != 0:
318 sys.stderr.write("Conversion failed. See %s\n" % tmpdir)
321 sys.stdout.write(file(os.path.join(tmpdir, "fields.png")).read())