Lots of new stuff again.  Moved buffer types to some separate files.
Added some new-fangled features to bgenOutput.  Generate doc strings!
diff --git a/Tools/bgen/bgen/bgen.py b/Tools/bgen/bgen/bgen.py
index aab35de..db28b0a 100644
--- a/Tools/bgen/bgen/bgen.py
+++ b/Tools/bgen/bgen/bgen.py
@@ -1,6 +1,11 @@
 "Export everything in the various bgen submodules."
 
 from bgenType import *
+from bgenVariable import *
+from bgenBuffer import *
+from bgenStackBuffer import *
+from bgenHeapBuffer import *
+from bgenStringBuffer import *
 from bgenOutput import *
 from bgenGenerator import *
 from bgenModule import *
diff --git a/Tools/bgen/bgen/bgenBuffer.py b/Tools/bgen/bgen/bgenBuffer.py
new file mode 100644
index 0000000..925b937
--- /dev/null
+++ b/Tools/bgen/bgen/bgenBuffer.py
@@ -0,0 +1,227 @@
+"""Buffers are character arrays that may contain null bytes.
+
+There are a number of variants depending on:
+- how the buffer is allocated (for output buffers), and
+- whether and how the size is passed into and/or out of the called function.
+"""
+
+
+from bgenType import Type, InputOnlyMixIn, OutputOnlyMixIn, InputOnlyType, OutputOnlyType
+from bgenOutput import *
+
+
+# Map common types to their format characters
+type2format = {
+	'long': 'l',
+	'int': 'i',
+	'short': 'h',
+	'char': 'b',
+	'unsigned long': 'l',
+	'unsigned int': 'i',
+	'unsigned short': 'h',
+	'unsigned char': 'b',
+}
+
+
+# ----- PART 1: Fixed character buffers -----
+
+
+class FixedInputOutputBufferType(InputOnlyType):
+	
+	"""Fixed buffer -- passed as (inbuffer, outbuffer)."""
+
+	def __init__(self, size, datatype = 'char', sizetype = 'int', sizeformat = None):
+		self.typeName = "Buffer"
+		self.size = str(size)
+		self.datatype = datatype
+		self.sizetype = sizetype
+		self.sizeformat = sizeformat or type2format[sizetype]
+
+	def declare(self, name):
+		self.declareBuffer(name)
+		self.declareSize(name)
+	
+	def declareBuffer(self, name):
+		self.declareInputBuffer(name)
+		self.declareOutputBuffer(name)
+	
+	def declareInputBuffer(self, name):
+		Output("%s *%s__in__;", self.datatype, name)
+	
+	def declareOutputBuffer(self, name):
+		Output("%s %s__out__[%s];", self.datatype, name, self.size)
+
+	def declareSize(self, name):
+		Output("%s %s__len__;", self.sizetype, name)
+
+	def getargsFormat(self):
+		# XXX This only works if the size is int-sized!!!
+		return "s#"
+
+	def getargsArgs(self, name):
+		return "&%s__in__, &%s__len__" % (name, name)
+	
+	def getargsCheck(self, name):
+		Output("if (%s__len__ != %s)", name, self.size)
+		OutLbrace()
+		Output('PyErr_SetString(PyExc_TypeError, "buffer length should be %s");',
+		       self.size)
+		Output("goto %s__error__;", name)
+		OutRbrace()
+
+	def passOutput(self, name):
+		return "%s__in__, %s__out__" % (name, name)
+	
+	def mkvalueFormat(self):
+		return "s#"
+
+	def mkvalueArgs(self, name):
+		return "%s__out__, %s" % (name, self.size)
+	
+	def cleanup(self, name):
+		DedentLevel()
+		Output(" %s__error__: ;", name)
+		IndentLevel()
+
+
+class FixedCombinedInputOutputBufferType(FixedInputOutputBufferType):
+	
+	"""Like fixed buffer -- but same parameter is input and output."""
+	
+	def passOutput(self, name):
+		return "(%s *)memcpy(%s__out__, %s__in__, %s)" % \
+			(self.datatype, name,   name,     self.size)
+
+
+class InputOnlyBufferMixIn(InputOnlyMixIn):
+
+	def declareOutputBuffer(self, name):
+		pass
+
+
+class OutputOnlyBufferMixIn(OutputOnlyMixIn):
+
+	def declareInputBuffer(self, name):
+		pass
+
+
+class FixedInputBufferType(InputOnlyBufferMixIn, FixedInputOutputBufferType):
+
+	"""Fixed size input buffer -- passed without size information.
+
+	Instantiate with the size as parameter.
+	"""
+
+	def passInput(self, name):
+		return "%s__in__" % name
+
+
+class FixedOutputBufferType(OutputOnlyBufferMixIn, FixedInputOutputBufferType):
+
+	"""Fixed size output buffer -- passed without size information.
+
+	Instantiate with the size as parameter.
+	"""
+
+	def passOutput(self, name):
+		return "%s__out__" % name
+
+
+class VarInputBufferType(FixedInputBufferType):
+
+	"""Variable size input buffer -- passed as (buffer, size).
+	
+	Instantiate without size parameter.
+	"""
+	
+	def __init__(self, datatype = 'char', sizetype = 'int', sizeformat = None):
+		FixedInputBufferType.__init__(self, "0", datatype, sizetype, sizeformat)
+	
+	def getargsCheck(self, name):
+		pass
+	
+	def passInput(self, name):
+		return "%s__in__, %s__len__" % (name, name)
+
+
+# ----- PART 2: Structure buffers -----
+
+
+class StructInputOutputBufferType(FixedInputOutputBufferType):
+	
+	"""Structure buffer -- passed as a structure pointer.
+
+	Instantiate with the struct type as parameter.
+	"""
+	
+	def __init__(self, type):
+		FixedInputOutputBufferType.__init__(self, "sizeof(%s)" % type)
+		self.typeName = self.type = type
+	
+	def declareInputBuffer(self, name):
+		Output("%s *%s__in__;", self.type, name)
+	
+	def declareOutputBuffer(self, name):
+		Output("%s %s__out__;", self.type, name)
+	
+	def getargsArgs(self, name):
+		return "(char **)&%s__in__, &%s__len__" % (name, name)
+	
+	def passInput(self, name):
+		return "%s__in__" % name
+	
+	def passOutput(self, name):
+		return "%s__in__, &%s__out__" % (name, name)
+	
+	def mkvalueArgs(self, name):
+		return "(char *)&%s__out__, %s" % (name, self.size)
+
+
+class StructCombinedInputOutputBufferType(StructInputOutputBufferType):
+
+	"""Like structure buffer -- but same parameter is input and output."""
+	
+	def passOutput(self, name):
+		return "(%s *)memcpy((char *)%s__out__, (char *)%s__in__, %s)" % \
+			(self.type,          name,              name,     self.size)
+
+
+class StructInputBufferType(InputOnlyBufferMixIn, StructInputOutputBufferType):
+
+	"""Fixed size input buffer -- passed as a pointer to a structure.
+
+	Instantiate with the struct type as parameter.
+	"""
+
+
+class StructByValueBufferType(StructInputBufferType):
+
+	"""Fixed size input buffer -- passed as a structure BY VALUE.
+
+	Instantiate with the struct type as parameter.
+	"""
+
+	def passInput(self, name):
+		return "*%s__in__" % name
+
+
+class StructOutputBufferType(OutputOnlyBufferMixIn, StructInputOutputBufferType):
+
+	"""Fixed size output buffer -- passed as a pointer to a structure.
+
+	Instantiate with the struct type as parameter.
+	"""
+
+	def passOutput(self, name):
+		return "&%s__out__" % name
+
+
+class ArrayOutputBufferType(OutputOnlyBufferMixIn, StructInputOutputBufferType):
+
+	"""Fixed size output buffer -- declared as a typedef, passed as an array.
+
+	Instantiate with the struct type as parameter.
+	"""
+
+	def passOutput(self, name):
+		return "%s__out__" % name
diff --git a/Tools/bgen/bgen/bgenGenerator.py b/Tools/bgen/bgen/bgenGenerator.py
index 62aab1c..4f57697 100644
--- a/Tools/bgen/bgen/bgenGenerator.py
+++ b/Tools/bgen/bgen/bgenGenerator.py
@@ -1,5 +1,6 @@
 from bgenOutput import *
 from bgenType import *
+from bgenVariable import *
 
 
 Error = "bgenGenerator.Error"
@@ -14,6 +15,7 @@
 class FunctionGenerator:
 
 	def __init__(self, returntype, name, *argumentList):
+		print "<--", name
 		self.returntype = returntype
 		self.name = name
 		self.argumentList = []
@@ -51,8 +53,42 @@
 	def reference(self, name = None):
 		if name is None:
 			name = self.name
-		Output("{\"%s\", (PyCFunction)%s_%s, 1},",
-		       name, self.prefix, self.name)
+		docstring = self.docstring()
+		Output("{\"%s\", (PyCFunction)%s_%s, 1,", name, self.prefix, self.name)
+		Output(" %s},", stringify(docstring))
+	
+	def docstring(self):
+		import string
+		input = []
+		output = []
+		for arg in self.argumentList:
+			if arg.flags == ErrorMode or arg.flags == SelfMode:
+				continue
+			if arg.type == None:
+				str = 'void'
+			else:
+				if hasattr(arg.type, 'typeName'):
+					typeName = arg.type.typeName
+					if typeName is None: # Suppressed type
+						continue
+				else:
+					typeName = "?"
+					print "Nameless type", arg.type
+					
+				str = typeName + ' ' + arg.name
+			if arg.mode in (InMode, InOutMode):
+				input.append(str)
+			if arg.mode in (InOutMode, OutMode):
+				output.append(str)
+		if not input:
+			instr = "()"
+		else:
+			instr = "(%s)" % string.joinfields(input, ", ")
+		if not output or output == ["void"]:
+			outstr = "None"
+		else:
+			outstr = "(%s)" % string.joinfields(output, ", ")
+		return instr + " -> " + outstr
 
 	def generate(self):
 		print "-->", self.name
@@ -143,9 +179,7 @@
 		tmp.reverse()
 		for arg in tmp:
 			if not arg: continue
-			if arg.flags == ErrorMode: continue
-			if arg.mode in (OutMode, InOutMode):
-				arg.mkvalueCleanup()
+			arg.cleanup()
 		Output("return _res;")
 
 	def functiontrailer(self):
@@ -174,6 +208,18 @@
 		Output("%s", self.body)
 		self.functiontrailer()
 
+_stringify_map = {'\n': '\\n', '\t': '\\t', '\r': '\\r', '\b': '\\b',
+                  '\e': '\\e', '\a': '\\a', '\f': '\\f', '"': '\\"'}
+def stringify(str):
+	if str is None: return "None"
+	res = '"'
+	map = _stringify_map
+	for c in str:
+		if map.has_key(c): res = res + map[c]
+		elif ' ' <= c <= '~': res = res + c
+		else: res = res + '\\%03o' % ord(c)
+	res = res + '"'
+	return res
 
 def _test():
 	void = None
diff --git a/Tools/bgen/bgen/bgenHeapBuffer.py b/Tools/bgen/bgen/bgenHeapBuffer.py
new file mode 100644
index 0000000..2c51695
--- /dev/null
+++ b/Tools/bgen/bgen/bgenHeapBuffer.py
@@ -0,0 +1,108 @@
+# Buffers allocated on the heap
+
+from bgenOutput import *
+from bgenType import OutputOnlyMixIn
+from bgenBuffer import FixedInputOutputBufferType
+
+
+class HeapInputOutputBufferType(FixedInputOutputBufferType):
+
+	"""Input-output buffer allocated on the heap -- passed as (inbuffer, outbuffer, size).
+
+	Instantiate without parameters.
+	Call from Python with input buffer.
+	"""
+
+	def __init__(self, datatype = 'char', sizetype = 'int', sizeformat = None):
+		FixedInputOutputBufferType.__init__(self, "0", datatype, sizetype, sizeformat)
+
+	def declareOutputBuffer(self, name):
+		Output("%s *%s__out__;", self.datatype, name)
+
+	def getargsCheck(self, name):
+		Output("if ((%s__out__ = malloc(%s__len__)) == NULL)", name, name)
+		OutLbrace()
+		Output('PyErr_NoMemory();')
+		Output("goto %s__error__;", name)
+		OutRbrace()
+
+	def passOutput(self, name):
+		return "%s__in__, %s__out__, %s__len__" % (name, name, name)
+
+	def mkvalueArgs(self, name):
+		return "%s__out__, %s__len__" % (name, name)
+
+	def cleanup(self, name):
+		Output("free(%s__out__);", name)
+		FixedInputOutputBufferType.cleanup(self, name)
+
+
+class VarHeapInputOutputBufferType(HeapInputOutputBufferType):
+
+	"""same as base class, but passed as (inbuffer, outbuffer, &size)"""
+	
+	def passOutput(self, name):
+		return "%s__in__, %s__out__, &%s__len__" % (name, name, name)
+
+
+class HeapCombinedInputOutputBufferType(HeapInputOutputBufferType):
+
+	"""same as base class, but passed as (inoutbuffer, size)"""
+	
+	def passOutput(self, name):
+		return "(%s *)memcpy(%s__out__, %s__in__, %s__len__)" % \
+			(self.datatype, name,   name,     name)
+
+
+class VarHeapCombinedInputOutputBufferType(HeapInputOutputBufferType):
+
+	"""same as base class, but passed as (inoutbuffer, &size)"""
+	
+	def passOutput(self, name):
+		return "(%s *)memcpy(%s__out__, %s__in__, &%s__len__)" % \
+			(self.datatype, name,   name,      name)
+
+
+class HeapOutputBufferType(OutputOnlyMixIn, HeapInputOutputBufferType):
+
+	"""Output buffer allocated on the heap -- passed as (buffer, size).
+
+	Instantiate without parameters.
+	Call from Python with buffer size.
+	"""
+	
+	def declareInputBuffer(self, name):
+		pass
+	
+	def getargsFormat(self):
+		return self.sizeformat
+	
+	def getargsArgs(self, name):
+		return "&%s__len__" % name
+	
+	def passOutput(self, name):
+		return "%s__out__, %s__len__" % (name, name)
+
+
+class VarHeapOutputBufferType(HeapOutputBufferType):
+
+	"""Output buffer allocated on the heap -- passed as (buffer, &size).
+
+	Instantiate without parameters.
+	Call from Python with buffer size.
+	"""
+
+	def passOutput(self, name):
+		return "%s__out__, &%s__len__" % (name, name)
+
+
+class VarVarHeapOutputBufferType(VarHeapOutputBufferType):
+
+	"""Output buffer allocated on the heap -- passed as (buffer, size, &size).
+
+	Instantiate without parameters.
+	Call from Python with buffer size.
+	"""
+
+	def passOutput(self, name):
+		return "%s__out__, %s__len__, &%s__len__" % (name, name, name)
diff --git a/Tools/bgen/bgen/bgenModule.py b/Tools/bgen/bgen/bgenModule.py
index 8824ee9..eda1a02 100644
--- a/Tools/bgen/bgen/bgenModule.py
+++ b/Tools/bgen/bgen/bgenModule.py
@@ -5,13 +5,13 @@
 
 	def __init__(self, name, prefix = None,
 		     includestuff = None,
-		     initstuff = None,
-		     preinitstuff = None):
+		     finalstuff = None,
+		     initstuff = None):
 		GeneratorGroup.__init__(self, prefix or name)
 		self.name = name
 		self.includestuff = includestuff
 		self.initstuff = initstuff
-		self.preinitstuff = preinitstuff
+		self.finalstuff = finalstuff
 
 	def addobject(self, od):
 		self.generators.append(od)
@@ -29,9 +29,9 @@
 
 		GeneratorGroup.generate(self)
 		
-		if self.preinitstuff:
+		if self.finalstuff:
 			Output()
-			Output("%s", self.preinitstuff)
+			Output("%s", self.finalstuff)
 
 		Output()
 		Output("void init%s()", self.name)
@@ -56,12 +56,17 @@
 		Output("static PyObject *%s;", self.errorname)
 
 	def createModuleVariables(self):
-		Output("""if ((%s = PyString_FromString("%s.Error")) == NULL ||""",
-		               self.errorname,           self.name)
+		Output("""%s = %s;""", self.errorname, self.exceptionInitializer())
+		Output("""if (%s == NULL ||""", self.errorname)
 		Output("""    PyDict_SetItemString(d, "Error", %s) != 0)""",
 		                                               self.errorname)
+		IndentLevel()
 		Output("""Py_FatalError("can't initialize %s.Error");""",
 		                                           self.name)
+		DedentLevel()
+
+	def exceptionInitializer(self):
+		return """PyString_FromString("%s.Error")""" % self.name
 
 
 def _test():
diff --git a/Tools/bgen/bgen/bgenObjectDefinition.py b/Tools/bgen/bgen/bgenObjectDefinition.py
index 10a4468..e7fa146 100644
--- a/Tools/bgen/bgen/bgenObjectDefinition.py
+++ b/Tools/bgen/bgen/bgenObjectDefinition.py
@@ -2,15 +2,25 @@
 from bgenGeneratorGroup import GeneratorGroup
 
 class ObjectDefinition(GeneratorGroup):
+	"Spit out code that together defines a new Python object type"
 
 	def __init__(self, name, prefix, itselftype):
-		import string
+		"""ObjectDefinition constructor.  May be extended, but do not override.
+		
+		- name: the object's official name, e.g. 'SndChannel'.
+		- prefix: the prefix used for the object's functions and data, e.g. 'SndCh'.
+		- itselftype: the C type actually contained in the object, e.g. 'SndChannelPtr'.
+		
+		XXX For official Python data types, rules for the 'Py' prefix are a problem.
+		"""
+		
 		GeneratorGroup.__init__(self, prefix or name)
 		self.name = name
 		self.itselftype = itselftype
 		self.objecttype = name + 'Object'
 		self.typename = name + '_Type'
 		self.argref = ""	# set to "*" if arg to <type>_New should be pointer
+		self.static = "static " # set to "" to make <type>_New and <type>_Convert public
 
 	def add(self, g):
 		g.setselftype(self.objecttype, self.itselftype)
@@ -25,17 +35,18 @@
 
 		OutHeader2("Object type " + self.name)
 
-		Output("staticforward PyTypeObject %s;", self.typename)
+		sf = self.static and "staticforward "
+		Output("%sPyTypeObject %s;", sf, self.typename)
 		Output()
 
 		Output("#define %s_Check(x) ((x)->ob_type == &%s)",
 		       self.prefix, self.typename)
 		Output()
 
-		Output("typedef struct {")
+		Output("typedef struct %s {", self.objecttype)
 		IndentLevel()
 		Output("PyObject_HEAD")
-		Output("%s ob_itself;", self.itselftype)
+		self.outputStructMembers()
 		DedentLevel()
 		Output("} %s;", self.objecttype)
 		Output()
@@ -56,8 +67,11 @@
 
 		OutHeader2("End object type " + self.name)
 
+	def outputStructMembers(self):
+		Output("%s ob_itself;", self.itselftype)
+
 	def outputNew(self):
-		Output("static PyObject *%s_New(itself)", self.prefix)
+		Output("%sPyObject *%s_New(itself)", self.static, self.prefix)
 		IndentLevel()
 		Output("const %s %sitself;", self.itselftype, self.argref)
 		DedentLevel()
@@ -66,28 +80,36 @@
 		self.outputCheckNewArg()
 		Output("it = PyObject_NEW(%s, &%s);", self.objecttype, self.typename)
 		Output("if (it == NULL) return NULL;")
-		Output("it->ob_itself = %sitself;", self.argref)
+		self.outputInitStructMembers()
 		Output("return (PyObject *)it;")
 		OutRbrace()
 		Output()
+
+	def outputInitStructMembers(self):
+		Output("it->ob_itself = %sitself;", self.argref)
 	
 	def outputCheckNewArg(self):
-		pass
-
+			"Override this method to apply additional checks/conversions"
+	
 	def outputConvert(self):
-		Output("""\
-static int %(prefix)s_Convert(v, p_itself)
-	PyObject *v;
-	%(itselftype)s *p_itself;
-{
-	if (v == NULL || !%(prefix)s_Check(v)) {
-		PyErr_SetString(PyExc_TypeError, "%(name)s required");
-		return 0;
-	}
-	*p_itself = ((%(objecttype)s *)v)->ob_itself;
-	return 1;
-}
-""" % self.__dict__)
+		Output("%s%s_Convert(v, p_itself)", self.static, self.prefix)
+		IndentLevel()
+		Output("PyObject *v;")
+		Output("%s *p_itself;", self.itselftype)
+		DedentLevel()
+		OutLbrace()
+		self.outputCheckConvertArg()
+		Output("if (!%s_Check(v))", self.prefix)
+		OutLbrace()
+		Output('PyErr_SetString(PyExc_TypeError, "%s required");', self.name)
+		Output("return 0;")
+		OutRbrace()
+		Output("*p_itself = ((%s *)v)->ob_itself;", self.objecttype)
+		Output("return 1;")
+		OutRbrace()
+
+	def outputCheckConvertArg(self):
+		"Override this method to apply additional conversions"
 
 	def outputDealloc(self):
 		Output("static void %s_dealloc(self)", self.prefix)
@@ -95,11 +117,14 @@
 		Output("%s *self;", self.objecttype)
 		DedentLevel()
 		OutLbrace()
-		self.outputFreeIt("self->ob_itself")
+		self.outputCleanupStructMembers()
 		Output("PyMem_DEL(self);")
 		OutRbrace()
 		Output()
 
+	def outputCleanupStructMembers(self):
+		self.outputFreeIt("self->ob_itself")
+
 	def outputFreeIt(self, name):
 		Output("/* Cleanup of %s goes here */", name)
 
@@ -126,7 +151,7 @@
 		Output()
 
 	def outputTypeObject(self):
-		Output("static PyTypeObject %s = {", self.typename)
+		Output("%sPyTypeObject %s = {", self.static, self.typename)
 		IndentLevel()
 		Output("PyObject_HEAD_INIT(&PyType_Type)")
 		Output("0, /*ob_size*/")
@@ -140,3 +165,11 @@
 		Output("(setattrfunc) %s_setattr, /*tp_setattr*/", self.prefix)
 		DedentLevel()
 		Output("};")
+
+
+class GlobalObjectDefinition(ObjectDefinition):
+	"Same as ObjectDefinition but exports its New and Create methods"
+
+	def __init__(self, name, prefix = None, itselftype = None):
+		ObjectDefinition.__init__(self, name, prefix or name, itselftype or name)
+		self.static = ""
diff --git a/Tools/bgen/bgen/bgenOutput.py b/Tools/bgen/bgen/bgenOutput.py
index 06ca3ed..1a512e5 100644
--- a/Tools/bgen/bgen/bgenOutput.py
+++ b/Tools/bgen/bgen/bgenOutput.py
@@ -2,20 +2,41 @@
 
 This should really be a class, but then everybody would be passing
 the output object to each other.  I chose for the simpler approach
-of a module with a global variable.  Use SetOutputFile() to change
-the output file.
+of a module with a global variable.  Use SetOutputFile() or
+SetOutputFileName() to change the output file.
 """
 
-def SetOutputFile(file = None):
-	"""Call this with an open file object to make that the output file.
+_NeedClose = 0
 
-	Call it without arguments to reset the output file to sys.stdout.
+def SetOutputFile(file = None, needclose = 0):
+	"""Call this with an open file object to make it the output file.
+
+	Call it without arguments to close the current file (if necessary)
+	and reset it to sys.stdout.
+	If the second argument is true, the new file will be explicitly closed
+	on a subsequence call.
 	"""
-	global _File
+	global _File, _NeedClose
+	if _NeedClose:
+		tmp = _File
+		_NeedClose = 0
+		_File = None
+		tmp.close()
 	if file is None:
 		import sys
 		file = sys.stdout
 	_File = file
+	_NeedClose = file and needclose
+
+def SetOutputFileName(filename = None):
+	"""Call this with a filename to make it the output file.
+	
+	Call it without arguments to close the current file (if necessary)
+	and reset it to sys.stdout.
+	"""
+	SetOutputFile()
+	if filename:
+		SetOutputFile(open(filename, 'w'), 1)
 
 SetOutputFile()	# Initialize _File
 
@@ -34,7 +55,10 @@
 	_Level = level
 
 def Output(format = "", *args):
-	"""Call this with a format string and arguments for the format.
+	VaOutput(format, args)
+
+def VaOutput(format, args):
+	"""Call this with a format string and and argument tuple for the format.
 
 	A newline is always added.  Each line in the output is indented
 	to the proper indentation level -- even if the result of the
@@ -64,15 +88,38 @@
 	_Level = _Level + by
 
 def DedentLevel(by = 1):
-	"""Decfrement the indentation level by one.
+	"""Decrement the indentation level by one.
 
 	When called with an argument, subtracts it from the indentation level.
 	"""
 	IndentLevel(-by)
 
-def OutLbrace():
-	"""Output a '{' on a line by itself and increase the indentation level."""
-	Output("{")
+def OutIndent(format = "", *args):
+	"""Combine Output() followed by IndentLevel().
+	
+	If no text is given, acts like lone IndentLevel().
+	"""
+	if format: VaOutput(format, args)
+	IndentLevel()
+
+def OutDedent(format = "", *args):
+	"""Combine Output() followed by DedentLevel().
+	
+	If no text is given, acts like loneDedentLevel().
+	"""
+	if format: VaOutput(format, args)
+	DedentLevel()
+
+def OutLbrace(format = "", *args):
+	"""Like Output, but add a '{' and increase the indentation level.
+	
+	If no text is given a lone '{' is output.
+	"""
+	if format:
+		format = format + " {"
+	else:
+		format = "{"
+	VaOutput(format, args)
 	IndentLevel()
 
 def OutRbrace():
@@ -95,22 +142,67 @@
 	"""Output a level 2 header comment (uses '-' dashes)."""
 	OutHeader(text, "-")
 
+def Out(text):
+	"""Output multiline text that's internally indented.
+	
+	Pass this a multiline character string.  The whitespace before the
+	first nonblank line of the string will be subtracted from all lines.
+	The lines are then output using Output(), but without interpretation
+	of formatting (if you need formatting you can do it before the call).
+	Recommended use:
+	
+		Out('''
+			int main(argc, argv)
+				int argc;
+				char *argv;
+			{
+				printf("Hello, world\\n");
+				exit(0);
+			}
+		''')
+	
+	Caveat: the indentation must be consistent -- if you use three tabs
+	in the first line, (up to) three tabs are removed from following lines,
+	but a line beginning with 24 spaces is not trimmed at all.  Don't use
+	this as a feature.
+	"""
+	# (Don't you love using triple quotes *inside* triple quotes? :-)
+	
+	import string
+	lines = string.splitfields(text, '\n')
+	indent = ""
+	for line in lines:
+		if string.strip(line):
+			for c in line:
+				if c not in string.whitespace:
+					break
+				indent = indent + c
+			break
+	n = len(indent)
+	for line in lines:
+		if line[:n] == indent:
+			line = line[n:]
+		else:
+			for c in indent:
+				if line[:1] <> c: break
+				line = line[1:]
+		VaOutput("%s", line)
+
 
 def _test():
 	"""Test program.  Run when the module is run as a script."""
 	OutHeader1("test bgenOutput")
-	Output("""
-#include <Python.h>
-#include <stdio.h>
-""")
-	Output("main(argc, argv)")
+	Out("""
+		#include <Python.h>
+		#include <stdio.h>
+	
+		main(argc, argv)
+			int argc;
+			char **argv;
+		{
+			int i;
+	""")
 	IndentLevel()
-	Output("int argc;")
-	Output("char **argv;")
-	DedentLevel()
-	OutLbrace()
-	Output("int i;")
-	Output()
 	Output("""\
 /* Here are a few comment lines.
    Just to test indenting multiple lines.
diff --git a/Tools/bgen/bgen/bgenStackBuffer.py b/Tools/bgen/bgen/bgenStackBuffer.py
new file mode 100644
index 0000000..b7df5bd
--- /dev/null
+++ b/Tools/bgen/bgen/bgenStackBuffer.py
@@ -0,0 +1,59 @@
+"""Buffers allocated on the stack."""
+
+
+from bgenBuffer import FixedInputBufferType, FixedOutputBufferType
+
+
+class StackOutputBufferType(FixedOutputBufferType):
+
+	"""Fixed output buffer allocated on the stack -- passed as (buffer, size).
+
+	Instantiate with the buffer size as parameter.
+	"""
+
+	def passOutput(self, name):
+		return "%s__out__, %s" % (name, self.size)
+
+
+class VarStackOutputBufferType(StackOutputBufferType):
+
+	"""Output buffer allocated on the stack -- passed as (buffer, &size).
+
+	Instantiate with the buffer size as parameter.
+	"""
+
+	def declareSize(self, name):
+		Output("int %s__len__ = %s;", name, self.size)
+
+	def passOutput(self, name):
+		return "%s__out__, &%s__len__" % (name, name)
+
+	def mkvalueArgs(self, name):
+		return "%s__out__, %s__len__" % (name, name)
+
+
+class VarVarStackOutputBufferType(VarStackOutputBufferType):
+
+	"""Output buffer allocated on the stack -- passed as (buffer, size, &size).
+
+	Instantiate with the buffer size as parameter.
+	"""
+
+	def passOutput(self, name):
+		return "%s__out__, %s__len__, &%s__len__" % (name, name, name)
+
+
+class ReturnVarStackOutputBufferType(VarStackOutputBufferType):
+
+	"""Output buffer allocated on the stack -- passed as (buffer, size) -> size.
+
+	Instantiate with the buffer size as parameter.
+	The function's return value is the size.
+	(XXX Should have a way to suppress returning it separately, too.)
+	"""
+
+	def passOutput(self, name):
+		return "%s__out__, %s__len__" % (name, name)
+
+	def mkvalueArgs(self, name):
+		return "%s__out__, _rv" % name
diff --git a/Tools/bgen/bgen/bgenStringBuffer.py b/Tools/bgen/bgen/bgenStringBuffer.py
new file mode 100644
index 0000000..7d9c77e
--- /dev/null
+++ b/Tools/bgen/bgen/bgenStringBuffer.py
@@ -0,0 +1,64 @@
+"""Buffers used to hold null-terminated strings."""
+
+
+from bgenBuffer import FixedOutputBufferType
+from bgenStackBuffer import StackOutputBufferType
+from bgenHeapBuffer import HeapOutputBufferType
+
+
+class StringBufferMixIn:
+
+	"""Mix-in class to create various string buffer types.
+
+	Strings are character arrays terminated by a null byte.
+	(For input, this is also covered by stringptr.)
+	For output, there are again three variants:
+	- Fixed: size is a constant given in the documentation; or
+	- Stack: size is passed to the C function but we decide on a size at
+	  code generation time so we can still allocate on the heap); or
+	- Heap: size is passed to the C function and we let the Python caller
+	  pass a size.
+	(Note that this doesn't cover output parameters in which a string
+	pointer is returned.  These are actually easier (no allocation) but far
+	less common.  I'll write the classes when there is demand.)
+	"""
+	
+	def declareSize(self, name):
+		pass
+	
+	def getargsFormat(self):
+		return "s"
+	
+	def getargsArgs(self, name):
+		return "&%s__in__" % name
+
+	def mkvalueFormat(self):
+		return "s"
+
+	def mkvalueArgs(self, name):
+		return "%s__out__" % name
+
+
+class FixedOutputStringType(StringBufferMixIn, FixedOutputBufferType):
+
+	"""Null-terminated output string -- passed without size.
+
+	Instantiate with buffer size as parameter.
+	"""
+
+
+class StackOutputStringType(StringBufferMixIn, StackOutputBufferType):
+
+	"""Null-terminated output string -- passed as (buffer, size).
+
+	Instantiate with buffer size as parameter.
+	"""
+
+
+class HeapOutputStringType(StringBufferMixIn, HeapOutputBufferType):
+
+	"""Null-terminated output string -- passed as (buffer, size).
+
+	Instantiate without parameters.
+	Call from Python with buffer size.
+	"""
diff --git a/Tools/bgen/bgen/bgenType.py b/Tools/bgen/bgen/bgenType.py
index 6518509..ba9a53e 100644
--- a/Tools/bgen/bgen/bgenType.py
+++ b/Tools/bgen/bgen/bgenType.py
@@ -1,22 +1,9 @@
-"""Type and Variable classes and a modest collection of standard types."""
+"""Type classes and a modest collection of standard types."""
+
 
 from bgenOutput import *
 
 
-# Values to represent argument transfer modes
-InMode    = 1 # input-only argument
-OutMode   = 2 # output-only argument
-InOutMode = 3 # input-output argument
-ModeMask  = 3 # bits to keep for mode
-
-
-# Special cases for mode/flags argument
-# XXX This is still a mess!
-SelfMode   =  4+InMode  # this is 'self' -- don't declare it
-ReturnMode =  8+OutMode # this is the function return value
-ErrorMode  = 16+OutMode # this is an error status -- turn it into an exception
-
-
 class Type:
 
 	"""Define the various things you can do with a C type.
@@ -106,8 +93,8 @@
 		"""
 		return name
 
-	def mkvalueCleanup(self, name):
-		"""Clean up if necessary after mkvalue().
+	def cleanup(self, name):
+		"""Clean up if necessary.
 
 		This is normally empty; it may deallocate buffers etc.
 		"""
@@ -120,7 +107,7 @@
 
 	"Mix-in class to boobytrap passOutput"
 
-	def passOutput(self):
+	def passOutput(self, name):
 		raise RuntimeError, "this type can only be used for input parameters"
 
 class InputOnlyType(InputOnlyMixIn, Type):
@@ -131,7 +118,7 @@
 
 	"Mix-in class to boobytrap passInput"
 
-	def passInput(self):
+	def passInput(self, name):
 		raise RuntimeError, "this type can only be used for output parameters"
 
 class OutputOnlyType(OutputOnlyMixIn, Type):
@@ -145,13 +132,14 @@
 short = Type("short", "h")
 int = Type("int", "i")
 long = Type("long", "l")
+unsigned_long = Type("unsigned long", "l")
 float = Type("float", "f")
 double = Type("double", "d")
 
 
 # The most common use of character pointers is a null-terminated string.
 # For input, this is easy.  For output, and for other uses of char *,
-# see the various Buffer types below.
+# see the module bgenBuffer.
 stringptr = InputOnlyType("char*", "s")
 
 
@@ -170,6 +158,7 @@
 
 	def __init__(self, substitute):
 		self.substitute = substitute
+		self.typeName = None	# Don't show this argument in __doc__ string
 
 	def declare(self, name):
 		pass
@@ -184,333 +173,49 @@
 		return self.substitute
 
 
-class AbstractBufferType:
-	"""Buffers are character arrays that may contain null bytes.
-
-	There are a number of variants depending on:
-	- how the buffer is allocated (for output buffers), and
-	- whether and how the size is passed into and/or out of the called function.
-
-	The AbstractbufferType only serves as a placeholder for this doc string.
-	"""
-
-	def declare(self, name):
-		self.declareBuffer(name)
-		self.declareSize(name)
-
-
-class FixedBufferType(AbstractBufferType):
-
-	"""Fixed size buffer -- passed without size information.
-
-	Instantiate with the size as parameter.
-	THIS IS STILL AN ABSTRACT BASE CLASS -- DO NOT INSTANTIATE.
-	"""
-
-	def __init__(self, size):
-		self.size = str(size)
-
-	def declareSize(self, name):
-		Output("int %s__len__;", name)
-
-
-class FixedInputBufferType(InputOnlyMixIn, FixedBufferType):
-
-	"""Fixed size input buffer -- passed without size information.
-
-	Instantiate with the size as parameter.
-	"""
-
-	def declareBuffer(self, name):
-		Output("char *%s;", name)
-
-	def getargsFormat(self):
-		return "s#"
-
-	def getargsArgs(self, name):
-		return "&%s, &%s__len__" % (name, name)
-
-	def getargsCheck(self, name):
-		Output("if (%s__len__ != %s)", name, self.size)
-		OutLbrace()
-		Output('PyErr_SetString(PyExc_TypeError, "buffer length should be %s");',
-		       self.size)
-		Output('return NULL;') # XXX should do a goto
-		OutRbrace()
-
-	def passInput(self, name):
-		return name
-
-
-class FixedOutputBufferType(OutputOnlyMixIn, FixedBufferType):
-
-	"""Fixed size output buffer -- passed without size information.
-
-	Instantiate with the size as parameter.
-	"""
-
-	def declareBuffer(self, name):
-		Output("char %s[%s];", name, self.size)
-
-	def passOutput(self, name):
-		return name
-
-	def mkvalueFormat(self):
-		return "s#"
-
-	def mkvalueArgs(self):
-		return "%s, %s" % (name, self.size)
-
-
-class StructBufferType(FixedBufferType):
-
-	"""Structure buffer -- passed as a structure pointer.
-
-	Instantiate with the struct type as parameter.
-	"""
-
-	def __init__(self, type):
-		FixedBufferType.__init__(self, "sizeof(%s)" % type)
-		self.type = type
-
-
-class StructInputBufferType(StructBufferType, FixedInputBufferType):
-
-	"""Fixed size input buffer -- passed as a pointer to a structure.
-
-	Instantiate with the struct type as parameter.
-	"""
-
-	def declareBuffer(self, name):
-		Output("%s *%s;", self.type, name)
-
-
-class StructByValueBufferType(StructInputBufferType):
-
-	"""Fixed size input buffer -- passed as a structure BY VALUE.
-
-	Instantiate with the struct type as parameter.
-	"""
-
-	def passInput(self, name):
-		return "*%s" % name
-
-
-class StructOutputBufferType(StructBufferType, FixedOutputBufferType):
-
-	"""Fixed size output buffer -- passed as a pointer to a structure.
-
-	Instantiate with the struct type as parameter.
-	"""
-
-	def declareBuffer(self, name):
-		Output("%s %s;", self.type, name)
-
-	def passOutput(self, name):
-		return "&%s" % name
-
-	def mkvalueArgs(self, name):
-		return "(char *)&%s" % name
-
-
-class VarInputBufferType(InputOnlyMixIn, FixedInputBufferType):
-
-	"""Input buffer -- passed as (buffer, size).
-
-	Instantiate without parameters.
-	"""
-
-	def __init__(self):
-		pass
-
-	def getargsCheck(self, name):
-		pass
-
-	def passInput(self, name):
-		return "%s, %s__len__" % (name, name)
-
-
-class StackOutputBufferType(OutputOnlyMixIn, FixedOutputBufferType):
-
-	"""Output buffer -- passed as (buffer, size).
-
-	Instantiate with the buffer size as parameter.
-	"""
-
-	def passOutput(self, name):
-		return "%s, %s" % (name, self.size)
-
-
-class VarStackOutputBufferType(StackOutputBufferType):
-
-	"""Output buffer allocated on the stack -- passed as (buffer, &size).
-
-	Instantiate with the buffer size as parameter.
-	"""
-
-	def declareSize(self, name):
-		Output("int %s__len__ = %s;", name, self.size)
-
-	def passOutput(self, name):
-		return "%s, &%s__len__" % (name, name)
-
-	def mkvalueArgs(self, name):
-		return "%s, %s__len__" % (name, name)
-
-
-class VarVarStackOutputBufferType(VarStackOutputBufferType):
-
-	"""Output buffer allocated on the stack -- passed as (buffer, size, &size).
-
-	Instantiate with the buffer size as parameter.
-	"""
-
-	def passOutput(self, name):
-		return "%s, %s__len__, &%s__len__" % (name, name, name)
-
-
-class HeapOutputBufferType(FixedOutputBufferType):
-
-	"""Output buffer allocated on the heap -- passed as (buffer, size).
-
-	Instantiate without parameters.
-	Call from Python with buffer size.
-	"""
-
-	def __init__(self):
-		pass
-
-	def declareBuffer(self, name):
-		Output("char *%s;", name)
-
-	def getargsFormat(self):
-		return "i"
-
-	def getargsArgs(self, name):
-		return "&%s__len__" % name
-
-	def getargsCheck(self, name):
-		Output("if ((%s = malloc(%s__len__)) == NULL) goto %s__error__;",
-		             name,       name,                     name)
-
-	def passOutput(self, name):
-		return "%s, %s__len__" % (name, name)
-
-	def mkvalueArgs(self, name):
-		return "%s, %s__len__" % (name, name)
-
-	def mkvalueCleanup(self, name):
-		Output("free(%s);", name)
-		DedentLevel()
-		Output(" %s__error__: ;", name);
-		IndentLevel()
-
-
-class VarHeapOutputBufferType(HeapOutputBufferType):
-
-	"""Output buffer allocated on the heap -- passed as (buffer, &size).
-
-	Instantiate without parameters.
-	Call from Python with buffer size.
-	"""
-
-	def passOutput(self, name):
-		return "%s, &%s__len__" % (name, name)
-
-
-class VarVarHeapOutputBufferType(VarHeapOutputBufferType):
-
-	"""Output buffer allocated on the heap -- passed as (buffer, size, &size).
-
-	Instantiate without parameters.
-	Call from Python with buffer size.
-	"""
-
-	def passOutput(self, name):
-		return "%s, %s__len__, &%s__len__" % (name, name, name)
-
-
-class StringBufferType:
-
-	"""Mix-in class to create various string buffer types.
-
-	Strings are character arrays terminated by a null byte.
-	For input, this is already covered by stringptr.
-	For output, there are again three variants:
-	- Fixed (size is a constant given in the documentation),
-	- Stack (size is passed to the C function but we decide on a size at
-	  code generation time so we can still allocate on the heap), or
-	- Heap (size is passed to the C function and we let the Python caller
-	  pass a size.
-	(Note that this doesn't cover output parameters in which a string
-	pointer is returned.  These are actually easier (no allocation) but far
-	less common.  I'll write the classes when there is demand.)
-	"""
-
-	def mkvalueFormat(self):
-		return "s"
-
-	def mkvalueArgs(self, name):
-		return name
-
-
-class FixedOutputStringType(StringBufferType, FixedOutputBufferType):
-
-	"""Null-terminated output string -- passed without size.
-
-	Instantiate with buffer size as parameter.
-	"""
-
-
-class StackOutputStringType(StringBufferType, StackOutputBufferType):
-
-	"""Null-terminated output string -- passed as (buffer, size).
-
-	Instantiate with buffer size as parameter.
-	"""
-
-class HeapOutputStringType(StringBufferType, HeapOutputBufferType):
-
-	"""Null-terminated output string -- passed as (buffer, size).
-
-	Instantiate without parameters.
-	Call from Python with buffer size.
-	"""
-
-
 class OpaqueType(Type):
 
 	"""A type represented by an opaque object type, always passed by address.
 
-	Instantiate with the type name, and optional an object type name whose
-	New/Convert functions will be used.
+	Instantiate with the type name and the names of the new and convert procs.
+	If fewer than three arguments are passed, the second argument is used
+	to derive the new and convert procs by appending _New and _Convert; it
+	defaults to the first argument.
 	"""
 
-	def __init__(self, name, sameAs = None):
+	def __init__(self, name, arg = None, extra = None):
 		self.typeName = name
-		self.sameAs = sameAs or name
+		if extra is None:
+			 # Two arguments (name, usetype) or one (name)
+			arg = arg or name
+			self.new = arg + '_New'
+			self.convert = arg + '_Convert'
+		else:
+			# Three arguments (name, new, convert)
+			self.new = arg 
+			self.convert = extra 
 
 	def getargsFormat(self):
-		return 'O&'
+		return "O&"
 
 	def getargsArgs(self, name):
-		return "%s_Convert, &%s" % (self.sameAs, name)
+		return "%s, &%s" % (self.convert, name)
 
 	def passInput(self, name):
 		return "&%s" % name
 
 	def mkvalueFormat(self):
-		return 'O&'
+		return "O&"
 
 	def mkvalueArgs(self, name):
-		return "%s_New, &%s" % (self.sameAs, name)
+		return "%s, &%s" % (self.new, name)
 
 
 class OpaqueByValueType(OpaqueType):
 
 	"""A type represented by an opaque object type, on input passed BY VALUE.
 
-	Instantiate with the type name, and optional an object type name whose
+	Instantiate with the type name, and optionally an object type name whose
 	New/Convert functions will be used.
 	"""
 
@@ -518,7 +223,7 @@
 		return name
 
 	def mkvalueArgs(self, name):
-		return "%s_New, %s" % (self.sameAs, name)
+		return "%s, %s" % (self.new, name)
 
 
 class OpaqueArrayType(OpaqueByValueType):
@@ -530,80 +235,7 @@
 	"""
 
 	def getargsArgs(self, name):
-		return "%s_Convert, &%s" % (self.sameAs, name)
+		return "%s, %s" % (self.convert, name)
 
 	def passOutput(self, name):
 		return name
-
-
-class Variable:
-
-	"""A Variable holds a type, a name, a transfer mode and flags.
-
-	Most of its methods call the correponding type method with the
-	variable name.
-	"""
-
-	def __init__(self, type, name = None, flags = InMode):
-		"""Call with a type, a name and flags.
-
-		If name is None, it muse be set later.
-		flags defaults to InMode.
-		"""
-		self.type = type
-		self.name = name
-		self.flags = flags
-		self.mode = flags & ModeMask
-
-	def declare(self):
-		"""Declare the variable if necessary.
-
-		If it is "self", it is not declared.
-		"""
-		if self.flags != SelfMode:
-			self.type.declare(self.name)
-
-	def getargsFormat(self):
-		"""Call the type's getargsFormatmethod."""
-		return self.type.getargsFormat()
-
-	def getargsArgs(self):
-		"""Call the type's getargsArgsmethod."""
-		return self.type.getargsArgs(self.name)
-
-	def getargsCheck(self):
-		return self.type.getargsCheck(self.name)
-
-	def passArgument(self):
-		"""Return the string required to pass the variable as argument.
-
-		For "in" arguments, return the variable name.
-		For "out" and "in out" arguments,
-		return its name prefixed with "&".
-		"""
-		if self.mode == InMode:
-			return self.type.passInput(self.name)
-		if self.mode in (OutMode, InOutMode):
-			return self.type.passOutput(self.name)
-		# XXX Shouldn't get here
-		return "/*mode?*/" + self.type.passInput(self.name)
-
-	def errorCheck(self):
-		"""Check for an error if necessary.
-
-		This only generates code if the variable's mode is ErrorMode.
-		"""
-		if self.flags == ErrorMode:
-			self.type.errorCheck(self.name)
-
-	def mkvalueFormat (self):
-		"""Call the type's mkvalueFormatmethod."""
-		return self.type.mkvalueFormat()
-
-	def mkvalueArgs(self):
-		"""Call the type's mkvalueArgs method."""
-		return self.type.mkvalueArgs(self.name)
-
-	def mkvalueCleanup(self):
-		"""Call the type's mkvalueCleanup method."""
-		return self.type.mkvalueCleanup(self.name)
diff --git a/Tools/bgen/bgen/bgenVariable.py b/Tools/bgen/bgen/bgenVariable.py
new file mode 100644
index 0000000..310cc8f
--- /dev/null
+++ b/Tools/bgen/bgen/bgenVariable.py
@@ -0,0 +1,88 @@
+"""Variables, arguments and argument transfer modes etc."""
+
+
+# Values to represent argument transfer modes
+InMode    = 1 # input-only argument
+OutMode   = 2 # output-only argument
+InOutMode = 3 # input-output argument
+ModeMask  = 3 # bits to keep for mode
+
+
+# Special cases for mode/flags argument
+# XXX This is still a mess!
+SelfMode   =  4+InMode  # this is 'self' -- don't declare it
+ReturnMode =  8+OutMode # this is the function return value
+ErrorMode  = 16+OutMode # this is an error status -- turn it into an exception
+
+
+class Variable:
+
+	"""A Variable holds a type, a name, a transfer mode and flags.
+
+	Most of its methods call the correponding type method with the
+	variable name.
+	"""
+
+	def __init__(self, type, name = None, flags = InMode):
+		"""Call with a type, a name and flags.
+
+		If name is None, it muse be set later.
+		flags defaults to InMode.
+		"""
+		self.type = type
+		self.name = name
+		self.flags = flags
+		self.mode = flags & ModeMask
+
+	def declare(self):
+		"""Declare the variable if necessary.
+
+		If it is "self", it is not declared.
+		"""
+		if self.flags != SelfMode:
+			self.type.declare(self.name)
+
+	def getargsFormat(self):
+		"""Call the type's getargsFormatmethod."""
+		return self.type.getargsFormat()
+
+	def getargsArgs(self):
+		"""Call the type's getargsArgsmethod."""
+		return self.type.getargsArgs(self.name)
+
+	def getargsCheck(self):
+		return self.type.getargsCheck(self.name)
+
+	def passArgument(self):
+		"""Return the string required to pass the variable as argument.
+
+		For "in" arguments, return the variable name.
+		For "out" and "in out" arguments,
+		return its name prefixed with "&".
+		"""
+		if self.mode == InMode:
+			return self.type.passInput(self.name)
+		if self.mode in (OutMode, InOutMode):
+			return self.type.passOutput(self.name)
+		# XXX Shouldn't get here
+		return "/*mode?*/" + self.type.passInput(self.name)
+
+	def errorCheck(self):
+		"""Check for an error if necessary.
+
+		This only generates code if the variable's mode is ErrorMode.
+		"""
+		if self.flags == ErrorMode:
+			self.type.errorCheck(self.name)
+
+	def mkvalueFormat (self):
+		"""Call the type's mkvalueFormat method."""
+		return self.type.mkvalueFormat()
+
+	def mkvalueArgs(self):
+		"""Call the type's mkvalueArgs method."""
+		return self.type.mkvalueArgs(self.name)
+
+	def cleanup(self):
+		"""Call the type's cleanup method."""
+		return self.type.cleanup(self.name)
diff --git a/Tools/bgen/bgen/macsupport.py b/Tools/bgen/bgen/macsupport.py
new file mode 100644
index 0000000..d2269b1
--- /dev/null
+++ b/Tools/bgen/bgen/macsupport.py
@@ -0,0 +1,140 @@
+"""\
+Augment the "bgen" package with definitions that are useful on the Apple Macintosh.
+
+Intended usage is "from macsupport import *" -- this implies all bgen's goodies.
+"""
+
+
+# Import everything from bgen (for ourselves as well as for re-export)
+from bgen import *
+
+
+# Simple types
+Boolean = Type("Boolean", "b")
+SignedByte = Type("SignedByte", "b")
+ScriptCode = Type("ScriptCode", "h")
+Size = Type("Size", "l")
+Style = Type("Style", "b")
+
+# Pascal strings
+ConstStr255Param = OpaqueArrayType("Str255", "PyMac_BuildStr255", "PyMac_GetStr255")
+Str255 = OpaqueArrayType("Str255", "PyMac_BuildStr255", "PyMac_GetStr255")
+
+# File System Specifications
+FSSpec_ptr = OpaqueType("FSSpec", "PyMac_BuildFSSpec", "PyMac_GetFSSpec")
+
+# OSType and ResType: 4-byte character strings
+def OSTypeType(typename):
+	return OpaqueByValueType(typename, "PyMac_BuildOSType", "PyMac_GetOSType")
+OSType = OSTypeType("OSType")
+ResType = OSTypeType("ResType")
+
+# Handles (always resources in our case)
+Handle = OpaqueByValueType("Handle", "ResObj")
+MenuHandle = OpaqueByValueType("MenuHandle", "MenuObj")
+ControlHandle = OpaqueByValueType("ControlHandle", "CtlObj")
+
+# Windows and Dialogs
+WindowPtr = OpaqueByValueType("WindowPtr", "WinObj")
+DialogPtr = OpaqueByValueType("DialogPtr", "DlgObj")
+
+# NULL pointer passed in as optional storage -- not present in Python version
+NullStorage = FakeType("(void *)0")
+
+# Quickdraw data types
+Rect = Rect_ptr = OpaqueType("Rect", "PyMac_BuildRect", "PyMac_GetRect")
+Point = OpaqueByValueType("Point", "PyMac_BuildPoint", "PyMac_GetPoint")
+
+# Event records
+EventRecord = OpaqueType("EventRecord", "PyMac_BuildEventRecord", "PyMac_GetEventRecord")
+EventRecord_ptr = EventRecord
+
+# OSErr is special because it is turned into an exception
+# (Could do this with less code using a variant of mkvalue("O&")?)
+class OSErrType(Type):
+	def errorCheck(self, name):
+		Output("if (%s != noErr) return PyMac_Error(%s);", name, name)
+		self.used = 1
+OSErr = OSErrType("OSErr", 'h')
+
+
+# Various buffer types
+
+InBuffer = VarInputBufferType('char', 'long', 'l')		# (buf, len)
+
+InOutBuffer = HeapInputOutputBufferType('char', 'long', 'l')	# (inbuf, outbuf, len)
+VarInOutBuffer = VarHeapInputOutputBufferType('char', 'long', 'l') # (inbuf, outbuf, &len)
+
+OutBuffer = HeapOutputBufferType('char', 'long', 'l')		# (buf, len)
+VarOutBuffer = VarHeapOutputBufferType('char', 'long', 'l')	# (buf, &len)
+VarVarOutBuffer = VarVarHeapOutputBufferType('char', 'long', 'l') # (buf, len, &len)
+
+
+# Predefine various pieces of program text to be passed to Module() later:
+
+# Stuff added immediately after the system include files
+includestuff = """
+#define SystemSevenOrLater 1
+
+#include "macglue.h"
+#include <Memory.h>
+#include <Dialogs.h>
+#include <Menus.h>
+#include <Controls.h>
+
+extern PyObject *ResObj_New(Handle);
+extern int ResObj_Convert(PyObject *, Handle *);
+
+extern PyObject *WinObj_New(WindowPtr);
+extern int WinObj_Convert(PyObject *, WindowPtr *);
+
+extern PyObject *DlgObj_New(DialogPtr);
+extern int DlgObj_Convert(PyObject *, DialogPtr *);
+extern PyTypeObject Dialog_Type;
+#define DlgObj_Check(x) ((x)->ob_type == &Dialog_Type)
+
+extern PyObject *MenuObj_New(MenuHandle);
+extern int MenuObj_Convert(PyObject *, MenuHandle *);
+
+extern PyObject *CtlObj_New(ControlHandle);
+extern int CtlObj_Convert(PyObject *, ControlHandle *);
+"""
+
+# Stuff added just before the module's init function
+finalstuff = """
+"""
+
+# Stuff added inside the module's init function
+initstuff = """
+"""
+
+
+# Generator classes with a twist -- if the function returns OSErr,
+# its mode is manipulated so that it turns into an exception or disappears
+# (and its name is changed to _err, for documentation purposes).
+# This requires that the OSErr type (defined above) has a non-trivial
+# errorCheck method.
+class OSErrMixIn:
+	"Mix-in class to treat OSErr return values special"
+	def makereturnvar(self):
+		if self.returntype is OSErr:
+			return Variable(self.returntype, "_err", ErrorMode)
+		else:
+			return Variable(self.returntype, "_rv", OutMode)
+
+class OSErrFunctionGenerator(OSErrMixIn, FunctionGenerator): pass
+class OSErrMethodGenerator(OSErrMixIn, MethodGenerator): pass
+
+
+class MacModule(Module):
+	"Subclass which gets the exception initializer from macglue.c"
+	def exceptionInitializer(self):
+		return "PyMac_GetOSErrException()"
+
+_SetOutputFileName = SetOutputFileName # Save original
+def SetOutputFileName(file = None):
+	"Set the output file name and set its creator&type to KAHL&TEXT"
+	_SetOutputFileName(file)
+	if file:
+		import MacOS
+		MacOS.SetCreatorAndType(file, 'KAHL', 'TEXT')
diff --git a/Tools/bgen/bgen/scantools.py b/Tools/bgen/bgen/scantools.py
new file mode 100644
index 0000000..d6ee88c
--- /dev/null
+++ b/Tools/bgen/bgen/scantools.py
@@ -0,0 +1,373 @@
+"""\
+Tools for scanning header files in search of function prototypes.
+
+Often, the function prototypes in header files contain enough information
+to automatically generate (or reverse-engineer) interface specifications
+from them.  The conventions used are very vendor specific, but once you've
+figured out what they are they are often a great help, and it sure beats
+manually entering the interface specifications.  (These are needed to generate
+the glue used to access the functions from Python.)
+
+In order to make this class useful, almost every component can be overridden.
+The defaults are (currently) tuned to scanning Apple Macintosh header files,
+although most Mac specific details are contained in header-specific subclasses.
+"""
+
+import regex
+import regsub
+import string
+import sys
+import os
+import fnmatch
+from types import *
+try:
+	import MacOS
+except ImportError:
+	MacOS = None
+
+# Default preferences
+CREATOR = 'KAHL'		# My favorite text editor on the Mac
+INCLUDEDIR = "D:Development:THINK C:Mac #includes:Apple #includes:"
+
+
+Error = "scantools.Error"
+
+class Scanner:
+
+	def __init__(self, input = None, output = None, defsoutput = None):
+		self.initsilent()
+		self.initblacklists()
+		self.initrepairinstructions()
+		self.initpaths()
+		self.initfiles()
+		self.initpatterns()
+		self.compilepatterns()
+		self.initosspecifics()
+		if output:
+			self.setoutput(output, defsoutput)
+		if input:
+			self.setinput(input)
+
+	def initsilent(self):
+		self.silent = 0
+
+	def error(self, format, *args):
+		if self.silent >= 0:
+			print format%args
+
+	def report(self, format, *args):
+		if not self.silent:
+			print format%args
+
+	def initblacklists(self):
+		self.blacklistnames = self.makeblacklistnames()
+		self.blacklisttypes = ["unknown"] + self.makeblacklisttypes()
+
+	def makeblacklistnames(self):
+		return []
+
+	def makeblacklisttypes(self):
+		return []
+
+	def initrepairinstructions(self):
+		self.repairinstructions = self.makerepairinstructions()
+
+	def makerepairinstructions(self):
+		return []
+
+	def initfiles(self):
+		self.specmine = 0
+		self.defsmine = 0
+		self.scanmine = 0
+		self.specfile = sys.stdout
+		self.defsfile = None
+		self.scanfile = sys.stdin
+		self.lineno = 0
+		self.line = ""
+
+	def initpaths(self):
+		self.includepath = [':', INCLUDEDIR]
+
+	def initpatterns(self):
+		self.head_pat = "^pascal[ \t]+" # XXX Mac specific!
+		self.tail_pat = "[);]"
+		self.whole_pat = "\(<type>[a-zA-Z0-9_]+\)[ \t\n]+" + \
+		                 "\(<name>[a-zA-Z0-9_]+\)[ \t\n]*(\(<args>[^()]*\))"
+		self.sym_pat = "^[ \t]*\(<name>[a-zA-Z0-9_]+\)[ \t]*=" + \
+		               "[ \t]*\(<defn>[-0-9'\"][^\t\n,]*\),?"
+		self.asplit_pat = "^\(<type>.*[^a-zA-Z0-9_]\)\(<name>[a-zA-Z0-9_]+\)$"
+
+	def compilepatterns(self):
+		for name in dir(self):
+			if name[-4:] == "_pat":
+				pat = getattr(self, name)
+				prog = regex.symcomp(pat)
+				setattr(self, name[:-4], prog)
+
+	def initosspecifics(self):
+		if MacOS:
+			self.filetype = 'TEXT'
+			self.filecreator = CREATOR
+		else:
+			self.filetype = self.filecreator = None
+
+	def setfiletype(self, filename):
+		if MacOS and (self.filecreator or self.filetype):
+			creator, type = MacOS.GetCreatorAndType(filename)
+			if self.filecreator: creator = self.filecreator
+			if self.filetype: type = self.filetype
+			MacOS.SetCreatorAndType(filename, creator, type)
+
+	def close(self):
+		self.closefiles()
+
+	def closefiles(self):
+		self.closespec()
+		self.closedefs()
+		self.closescan()
+
+	def closespec(self):
+		tmp = self.specmine and self.specfile
+		self.specfile = None
+		if tmp: tmp.close()
+
+	def closedefs(self):
+		tmp = self.defsmine and self.defsfile
+		self.defsfile = None
+		if tmp: tmp.close()
+
+	def closescan(self):
+		tmp = self.scanmine and self.scanfile
+		self.scanfile = None
+		if tmp: tmp.close()
+
+	def setoutput(self, spec, defs = None):
+		self.closespec()
+		self.closedefs()
+		if spec:
+			if type(spec) == StringType:
+				file = self.openoutput(spec)
+				mine = 1
+			else:
+				file = spec
+				mine = 0
+			self.specfile = file
+			self.specmine = mine
+		if defs:
+			if type(defs) == StringType:
+				file = self.openoutput(defs)
+				mine = 1
+			else:
+				file = defs
+				mine = 0
+			self.defsfile = file
+			self.defsmine = mine
+
+	def openoutput(self, filename):
+		file = open(filename, 'w')
+		self.setfiletype(filename)
+		return file
+
+	def setinput(self, scan = sys.stdin):
+		self.closescan()
+		if scan:
+			if type(scan) == StringType:
+				file = self.openinput(scan)
+				mine = 1
+			else:
+				file = scan
+				mine = 0
+			self.scanfile = file
+			self.scanmine = mine
+		self.lineno = 0
+
+	def openinput(self, filename):
+		if not os.path.isabs(filename):
+			for dir in self.includepath:
+				fullname = os.path.join(dir, filename)
+				#self.report("trying full name %s", `fullname`)
+				try:
+					return open(fullname, 'r')
+				except IOError:
+					pass
+		# If not on the path, or absolute, try default open()
+		return open(filename, 'r')
+
+	def getline(self):
+		if not self.scanfile:
+			raise Error, "input file not set"
+		self.line = self.scanfile.readline()
+		if not self.line:
+			raise EOFError
+		self.lineno = self.lineno + 1
+		return self.line
+
+	def scan(self):
+		if not self.scanfile:
+			self.error("No input file has been specified")
+			return
+		inputname = self.scanfile.name
+		self.report("scanfile = %s", `inputname`)
+		if not self.specfile:
+			self.report("(No interface specifications will be written)")
+		else:
+			self.report("specfile = %s", `self.specfile.name`)
+			self.specfile.write("# Generated from %s\n" % `inputname`)
+		if not self.defsfile:
+			self.report("(No symbol definitions will be written)")
+		else:
+			self.report("defsfile = %s", `self.defsfile.name`)
+			self.defsfile.write("# Generated from %s\n" % `inputname`)
+		self.alreadydone = []
+		try:
+			while 1:
+				try: line = self.getline()
+				except EOFError: break
+				if self.defsfile and self.sym.match(line) >= 0:
+					self.dosymdef()
+					continue
+				if self.head.match(line) >= 0:
+					self.dofuncspec()
+					continue
+		except EOFError:
+			self.error("Uncaught EOF error")
+
+	def dosymdef(self):
+		name, defn = self.sym.group('name', 'defn')
+		self.defsfile.write("%s = %s\n" % (name, defn))
+
+	def dofuncspec(self):
+		raw = self.line[len(self.head.group(0)):]
+		while self.tail.search(raw) < 0:
+			line = self.getline()
+			raw = raw + line
+		self.processrawspec(raw)
+
+	def processrawspec(self, raw):
+		if self.whole.search(raw) < 0:
+			self.report("Bad raw spec: %s", `raw`)
+			return
+		type, name, args = self.whole.group('type', 'name', 'args')
+		if name in self.alreadydone:
+			self.report("Name has already been defined: %s", `name`)
+			return
+		self.report("==> %s %s <==", type, name)
+		if self.blacklisted(type, name):
+			self.error("*** %s %s blacklisted", type, name)
+			return
+		arglist = self.extractarglist(args)
+		arglist = self.repairarglist(arglist)
+		if self.unmanageable(type, name, arglist):
+			##for arg in arglist:
+			##	self.report("    %s", `arg`)
+			self.error("*** %s %s unmanageable", type, name)
+			return
+		self.alreadydone.append(name)
+		self.generate(type, name, arglist)
+
+	def extractarglist(self, args):
+		args = string.strip(args)
+		if not args or args == "void":
+			return []
+		parts = map(string.strip, string.splitfields(args, ","))
+		arglist = []
+		for part in parts:
+			arg = self.extractarg(part)
+			arglist.append(arg)
+		return arglist
+
+	def extractarg(self, part):
+		mode = "InMode"
+		if self.asplit.match(part) < 0:
+			self.error("Indecipherable argument: %s", `part`)
+			return ("unknown", part, mode)
+		type, name = self.asplit.group('type', 'name')
+		type = regsub.gsub("\*", " ptr ", type)
+		type = string.strip(type)
+		type = regsub.gsub("[ \t]+", "_", type)
+		if type[:6] == "const_":
+			type = type[6:]
+		elif type[-4:] == "_ptr":
+			type = type[:-4]
+			mode = "OutMode"
+		return type, name, mode
+
+	def repairarglist(self, arglist):
+		arglist = arglist[:]
+		i = 0
+		while i < len(arglist):
+			for pattern, replacement in self.repairinstructions:
+				n = len(pattern)
+				if i+n > len(arglist): continue
+				current = arglist[i:i+n]
+				for j in range(n):
+					if not self.matcharg(pattern[j], current[j]):
+						break
+				else: # All items of the pattern match
+					new = self.substituteargs(
+							pattern, replacement, current)
+					if new is not None:
+						arglist[i:i+n] = new
+						i = i+len(new) # No recursive substitutions
+						break
+			else: # No patterns match
+				i = i+1
+		return arglist
+	
+	def matcharg(self, patarg, arg):
+		return len(filter(None, map(fnmatch.fnmatch, arg, patarg))) == 3
+
+	def substituteargs(self, pattern, replacement, old):
+		new = []
+		for k in range(len(replacement)):
+			item = replacement[k]
+			newitem = [item[0], item[1], item[2]]
+			for i in range(3):
+				if item[i] == '*':
+					newitem[i] = old[k][i]
+				elif item[i][:1] == '$':
+					index = string.atoi(item[i][1:]) - 1
+					newitem[i] = old[index][i]
+			new.append(tuple(newitem))
+		##self.report("old: %s", `old`)
+		##self.report("new: %s", `new`)
+		return new
+
+	def generate(self, type, name, arglist):
+		classname, listname = self.destination(type, name, arglist)
+		if not self.specfile: return
+		self.specfile.write("\nf = %s(%s, %s,\n" % (classname, type, `name`))
+		for atype, aname, amode in arglist:
+			self.specfile.write("    (%s, %s, %s),\n" %
+			                    (atype, `aname`, amode))
+		self.specfile.write(")\n")
+		self.specfile.write("%s.append(f)\n" % listname)
+
+	def destination(self, type, name, arglist):
+		return "FunctionGenerator", "functions"
+
+	def blacklisted(self, type, name):
+		if type in self.blacklisttypes:
+			##self.report("return type %s is blacklisted", type)
+			return 1
+		if name in self.blacklistnames:
+			##self.report("function name %s is blacklisted", name)
+			return 1
+		return 0
+
+	def unmanageable(self, type, name, arglist):
+		for atype, aname, amode in arglist:
+			if atype in self.blacklisttypes:
+				self.report("argument type %s is blacklisted", atype)
+				return 1
+		return 0
+
+def test():
+	input = "D:Development:THINK C:Mac #includes:Apple #includes:AppleEvents.h"
+	output = "@aespecs.py"
+	defsoutput = "@aedefs.py"
+	s = Scanner(input, output, defsoutput)
+	s.scan()
+
+if __name__ == '__main__':
+	test()