File: generate

package info (click to toggle)
libfiu 1.2-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 768 kB
  • sloc: ansic: 2,633; python: 973; makefile: 599; sh: 309
file content (363 lines) | stat: -rwxr-xr-x 8,874 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
#!/usr/bin/env python3

"""
Reads function information and generates code for the preloader library.

The code is NOT nice. It just does the trick.
"""

import sys
import re
import copy


# Function definition regular expression
func_def_re = re.compile(
	r'(?P<ret_type>(?:[\w\*]+\s+\**)+)+(?P<name>\w+).*\((?P<params>.*)\).*;')

# Regular expression to extract the types and names of the parameters from a
# string containing the definition parameters (e.g. from
# "int a, const char *b" extracts [('int ', 'a'), ('const char *', 'b')]
params_info_re = \
	re.compile(r"(?:(?P<type>(?:[\w\*]+\s+\**)+)+(?P<name>\w+),?\s*)+?")


class Context:
	"""Represents the current context information within a module
	definition file."""
	def __init__(self):
		self.fiu_name_base = 'UNKNOWN'


class Function:
	"Represents a function to be wrapped"

	def __init__(self, definition, ctx):
		"Constructor, takes the C definition as a string"
		self.definition = definition
		self.load_from_definition(definition)

		# fiu name, constructed by default from the context but can be
		# overriden by info
		self.fiu_name = ctx.fiu_name_base + '/' + self.name

		# what to return on error, by default set to None, which means
		# "take it from failinfo"
		self.on_error = None

		# whether to set errno or not, and the list of valid errnos;
		# in any case if failinfo is set we take the errno value from
		# there
		self.use_errno = False
		self.valid_errnos = []

		# the FILE * for which a future ferror() shall fail too.
		self.ferror = None

		# if the given parameter should be reduced by a random amount
		self.reduce = None

		# describes possible variations of function, for example
		# pread() and pread64().
		self.variants = []

	def load_from_definition(self, definition):
		m = func_def_re.match(definition)
		self.name = m.group("name")
		self.ret_type = m.group("ret_type")
		self.params =  m.group("params")
		self.params_info = params_info_re.findall(self.params)

	def load_info(self, info):
		"Loads additional information from the given string"
		if ':' in info:
			s = info.split(':', 1)
			k, v = s[0].strip(), s[1].strip()

			if k == 'fiu name':
				self.fiu_name = v
			elif k == 'on error':
				self.on_error = v
			elif k == 'valid errnos':
				self.use_errno = True
				self.valid_errnos = v.split()
			elif k == 'ferror':
				self.ferror = v
			elif k == 'reduce':
				self.reduce = v
			elif k == 'variants':
				self.variants = v.split();
			else:
				raise SyntaxError("Unknown information: " + k)

	def __repr__(self):
		s = '<F %(rt)s %(n)s ( %(p)s ) -- %(fn)s %(oe)s %(ve)s>' % \
			{
				'rt': self.ret_type,
				'n': self.name,
				'p': self.params,
				'fn': self.fiu_name,
				'oe': self.on_error,
				've': str(self.valid_errnos),
			}
		return s

	def generate_to(self, f):
		"""Generates code to the given file. Strongly related to
		codegen.h."""
		f.write('/* Wrapper for %s() */\n' % self.name)

		# extract params names and types
		paramst = ', '.join([i[0] for i in self.params_info])
		paramsn = ', '.join([i[1] for i in self.params_info])

		f.write('mkwrap_top(%s, %s, (%s), (%s), (%s), (%s))\n' % \
				(self.ret_type, self.name, self.params,
					paramsn, paramst, self.on_error) )

		if self.reduce:
			f.write('mkwrap_body_reduce("%s/reduce", %s)\n' % \
					(self.fiu_name, self.reduce) )

		if self.use_errno:
			if self.on_error is None:
				desc = "%s uses errno but has no on_error" % \
					self.name
				raise RuntimeError(desc)

			# We can't put this as a macro parameter, so it has to
			# be explicit 
			self.write_valid_errnos(f)

			if self.ferror is not None:
				f.write('mkwrap_body_errno_ferror("%s", %s, %s)\n' % \
						(self.fiu_name, self.on_error, self.ferror) )
			else:
				f.write('mkwrap_body_errno("%s", %s)\n' % \
						(self.fiu_name, self.on_error) )
		elif self.on_error is not None:
			f.write('mkwrap_body_hardcoded("%s", %s)\n' % \
					(self.fiu_name, self.on_error) )
		else:
			f.write('mkwrap_body_failinfo("%s", %s)\n' % \
					(self.fiu_name, self.ret_type) )

		f.write('mkwrap_bottom(%s, (%s))\n' % (self.name, paramsn))
		f.write('\n\n')

	def write_valid_errnos(self, f):
		"Generates the code for the static list of valid errnos."
		f.write("\tstatic const int valid_errnos[] = {\n")
		for e in self.valid_errnos:
			f.write("\t  #ifdef %s\n" % e)
			f.write("\t\t%s,\n" % e)
			f.write("\t  #endif\n")
		f.write("\t};\n");

	def fiu_names(self):
		n = [self.fiu_name]
		if self.reduce:
			n.append(self.fiu_name + '/reduce')
		return n

	def apply_variant(self, v):
		if v != 'off64_t':
			raise SyntaxError("Unknown function variant: " + v)

		f = copy.copy(self)

		# NOTE: We don't modify fiu_name here to be able to enable
		# both <func> and <func>64 versions of the function by
		# enabling just <func>.
		f.name = f.name + "64"
		f.params = f.params.replace("off_t", "off64_t")
		f.params = f.params.replace("fpos_t *", "fpos64_t *")
		f.params = f.params.replace("const fpos_t *", "const fpos64_t *")
		f.params_info = [
			(x, y) if x != "off_t " else ("off64_t ", y)
			for (x, y) in f.params_info]
		f.params_info = [
			(x, y) if x != "fpos_t *" else ("fpos64_t *", y)
			for (x, y) in f.params_info]
		f.params_info = [
			(x, y) if x != "const fpos_t *" else ("const fpos64_t *", y)
			for (x, y) in f.params_info]
		f.ret_type = f.ret_type.replace("off_t", "off64_t")

		# This is glibc-specific, so surround it with #ifdefs.
		return [Verbatim("#ifdef __GLIBC__"), f, Verbatim("#endif")]

	def get_all_variants(self):
		"""Returns all variants of the given function provided via
		'variants:' function directive"""
		variants = [self]
		for v in self.variants:
			variants.extend(self.apply_variant(v))
		return variants

class Include:
	"Represents an include directive"
	def __init__(self, path):
		self.path = path

	def __repr__(self):
		return '<I %s>' % self.path

	def generate_to(self, f):
		f.write("#include %s\n" % self.path)

class Verbatim:
	"Represent a verbatim directive"
	def __init__(self, line):
		self.line = line

	def __repr__(self):
		return '<V %s>' % self.line

	def generate_to(self, f):
		f.write(self.line + '\n')

class EmptyLine:
	"Represents an empty line"
	def __repr__(self):
		return '<E>'

	def generate_to(self, f):
		f.write('\n')

class Comment:
	"Represents a full-line comment"
	def __init__(self, line):
		self.body = line.strip()[1:].strip()

	def __repr__(self):
		return '<C %s>' % self.body

	def generate_to(self, f):
		f.write("// %s \n" % self.body)


def parse_module(path):
	"Parses a module definition"

	f = open(path)

	directives = []
	ctx = Context()
	current_func = None

	while True:
		l = f.readline()

		# handle EOF
		if not l:
			break

		# handle \ at the end of the line
		while l.endswith("\\\n"):
			nl = f.readline()
			l = l[:-2] + nl

		if not l.strip():
			directives.append(EmptyLine())
			continue

		if l.strip().startswith("#"):
			directives.append(Comment(l))
			continue


		if not l.startswith(" ") and not l.startswith("\t"):
			# either a new function or a directive, but in either
			# case the current function is done
			if current_func:
				directives.extend(current_func.get_all_variants())
			current_func = None

			l = l.strip()

			if ':' in l:
				# directive
				s = l.split(':', 1)
				k, v = s[0].strip(), s[1].strip()
				if k == 'fiu name base':
					v = v.strip().strip('/')
					ctx.fiu_name_base = v
				elif k == 'include':
					directives.append(Include(v))
				elif k == 'v':
					directives.append(Verbatim(v))
				else:
					raise SyntaxError("Unknown directive", l)
			else:
				current_func = Function(l, ctx)
		else:
			# function information
			current_func.load_info(l.strip())

	if current_func:
		directives.extend(current_func.get_all_variants())

	return directives


#
# Code generation
#

# Templates

gen_header = """
/*
 * AUTOGENERATED FILE - DO NOT EDIT
 *
 * This file was automatically generated by libfiu, do not edit it directly,
 * but see libfiu's "preload" directory.
 */

#include "codegen.h"

"""


def generate_code(directives, path):
	"""Generates code to the file in the given path"""
	f = open(path, 'w')

	f.write(gen_header)

	for directive in directives:
		directive.generate_to(f)


def write_function_list(directives, path):
	"Writes the function list to the given path"
	f = open(path, 'a')
	for d in directives:
		if isinstance(d, Function):
			f.write("%-32s%s\n" % (d.name, \
				', '.join(d.fiu_names())) )


def usage():
	print("Use: ./generate input.mod output.c file_list.fl")

def main():
	if len(sys.argv) < 4:
		usage()
		sys.exit(1)

	input_name = sys.argv[1]
	output_name = sys.argv[2]
	filelist_name = sys.argv[3]

	directives = parse_module(input_name)
	#import pprint
	#pprint.pprint(directives)

	generate_code(directives, output_name)
	write_function_list(directives, filelist_name)

if __name__ == '__main__':
	main()