Initial revision
diff --git a/Mac/Contrib/PythonScript/PyScriptTest.py b/Mac/Contrib/PythonScript/PyScriptTest.py
new file mode 100644
index 0000000..fee3203
--- /dev/null
+++ b/Mac/Contrib/PythonScript/PyScriptTest.py
@@ -0,0 +1,25 @@
+'''
+Minimal test module
+'''#
+
+import sys
+import PythonScript
+
+SIGNATURE = 'MACS'
+TIMEOUT = 10*60*60
+
+PythonScript.PsScript(SIGNATURE, TIMEOUT)
+p = PythonScript.PyScript
+ev = PythonScript.PsEvents
+pc = PythonScript.PsClass
+pp = PythonScript.PsProperties
+
+startup = str(p(ev.Get, pc.Desktopobject(1).Startup_disk().Name()))
+print 'startup',startup, type(startup)
+print p(ev.Get, pc.Disk(startup).Folder(7).File(1).Name())
+print p(ev.Get, pc.Disk(1).Name())
+print p(ev.Get, pc.Disk('every').Name())
+print p(ev.Make, None, New='Alias_file', At=pp.Desktop(''), To=pp.System_folder(1))
+
+sys.exit(1)
+	
diff --git a/Mac/Contrib/PythonScript/PythonScript.py b/Mac/Contrib/PythonScript/PythonScript.py
new file mode 100644
index 0000000..b6077ae
--- /dev/null
+++ b/Mac/Contrib/PythonScript/PythonScript.py
@@ -0,0 +1,301 @@
+"""
+Python script a module to comunicate with apple events
+
+v 0.1a2
+v.0.2 16 april 1998 
+
+
+"""
+import sys
+import getaete
+import baetools
+import baetypes
+import AE
+import AppleEvents
+import macfs
+from types import *
+#from aetypes import InstanceType
+from aepack import AEDescType
+
+ordinal = {
+'every': 'all ',
+'first' : 'firs',
+'last' : 'last',
+'any' : 'any ',
+'middle' : 'midd'}
+
+
+Error = 'PythonScript.Error'
+
+
+class PsEvents:
+	pass
+
+
+class PsClasses:
+
+	
+	def __getattr__(self, name):
+		try:
+			return DCItem(name, self)
+		except:
+			pass
+		
+	def __repr__(self):
+		if self.form != 'prop':
+			t = type(self.seld)
+			if t == StringType:
+				self.form = 'name'
+			elif baetypes.IsRange(self.seld):
+				self.form = 'rang'
+			elif baetypes.IsComparison(self.seld) or baetypes.IsLogical(self.seld):
+				self.form = 'test'
+			elif t == TupleType:
+			# Breakout: specify both form and seld in a tuple
+			# (if you want ID or rele or somesuch)
+				self.form, self.seld = self.seld
+			elif t == IntType:
+				self.form = 'indx'
+			else:
+				pass
+
+		if self.seld in ordinal.keys():
+			self.seld = baetypes.Ordinal(ordinal[self.seld])
+			self.form = 'indx'
+
+		s = "baetypes.ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`)
+		if `self.fr`:
+			s = s + ", %s)" % `self.fr`
+		else:
+			s = s + ")"
+		return s
+		
+	def __str__(self):
+		return self.want
+		
+		
+def template(self, seld=None, fr=None):
+	self.seld = seld
+	self.fr = fr
+		
+def template1(self, which, fr=None):
+	self.want = 'prop'
+	self.form = 'prop'
+	self.fr = fr
+	
+class DCItem:
+	def __init__(self, comp, fr):
+		self.compclass = comp
+		self.fr = fr
+		
+	def __call__(self, which=None):
+		if which:
+			self.compclass = eval('PsClass.%s' % self.compclass)
+		else:
+			try:
+				self.compclass = eval('PsProperties.%s' % self.compclass)
+			except AttributeError:
+				self.compclass = eval('PsClass.%s' % self.compclass)
+		return self.compclass(which, self.fr)
+
+class PsClass:
+	pass
+
+class PsProperties:
+	pass				
+
+
+class PsEnumerations:
+	pass
+
+			
+def PsScript(sig=None, Timeout=0, Ignoring=0):
+	elements = {}
+	if sig:
+		target, sig = Signature(sig)
+		pyscript = getaete.Getaete(sig)
+	else:
+		target, sig = Signature('Pyth')
+		pyscript = getaete.Getaete()
+	setattr(PyScript, 'timeout', Timeout)
+	setattr(PyScript, 'ignoring', Ignoring)
+	setattr(PyScript, 'target', target)
+	for key, value in pyscript[0].items():
+		setattr(PsEvents, key, value)
+	for key, value in pyscript[1].items():
+		CreateClass(key, 'PsClasses', value)
+		for val in value[2]:
+			CreateProperty(val[0], 'PsClasses', `val[1]`)
+			
+		if value[3]:
+			for val in value[3]:
+				if val[0] not in elements.keys():
+					elements[val[0]] = val[1]
+				elif len(val[1]) > len(elements[val[0]]):
+					elements[val[0]] = val[1]
+
+	for key, value in pyscript[2].items():
+		for val in value:
+			setattr(PsEnumerations, val[0], val[1])
+
+def CreateClass(newClassName, superClassName, value):
+	parentDict = PsClass.__dict__
+	exec "class %s(%s): pass" % (newClassName, superClassName) in \
+			globals(), parentDict
+	newClassObj = parentDict[newClassName]
+	newClassObj.__init__ = template
+	exec "setattr(newClassObj, 'want', %s)" % `value[0]`
+	if value[2] and value[2][0][0] == 'every':
+		exec "setattr(newClassObj, 'plur', 1)"
+
+def CreateProperty(newClassName, superClassName, value):
+	parentDict = PsProperties.__dict__
+	exec "class %s(%s): pass" % (newClassName, superClassName) in \
+			globals(), parentDict
+	newClassObj = parentDict[newClassName]
+	if newClassName == 'Every':
+		value = "baetypes.mkOrdinal('every')"
+	newClassObj.__init__ = template1
+	exec "setattr(newClassObj, 'seld', %s)" % value
+
+def Signature(signature):
+	if type(signature) == AEDescType:
+		target = signature
+	elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
+		target = signature.__aepack__()
+	elif type(signature) == StringType:
+		if len(signature) == 4:
+			target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
+			target_signature = signature
+		else:
+			#This should ready be made persistant, so PythonScript 'remembered' where applications were
+			fss, ok = macfs.PromptGetFile('Find the aplication %s' % signature, 'APPL')
+			if ok:
+				target_signature = fss.GetCreatorType()[0]
+				target = AE.AECreateDesc(AppleEvents.typeApplSignature, target_signature)
+	else:
+		raise TypeError, "signature should be 4-char string or AEDesc"
+	return target, target_signature
+
+		
+
+		
+class PyScript(PsEvents):
+	def __init__(self, name, obj=None,  **args):
+		desc, code, subcode, rply, message, keywds = name
+#		print 'code', code
+#		print 'subcode', subcode
+#		print 'rply', rply
+#		print 'message', message
+#		print 'keywds', keywds
+#		print 'name', name
+#		print 'obj', obj
+#		print 'args', args
+		self.code = code
+		self.subcode = subcode
+		self.attributes ={}
+		self.arguments = {}
+		if keywds:
+			self.arguments = self.keyargs(keywds, args)
+		self.arguments['----'] = self.keyfms(message[0], obj)
+
+		##XXXX Eudora needs this XXXX##
+		if self.arguments['----'] == None:
+			del self.arguments['----']
+#		print 'arguments', self.arguments
+		if self.ignoring or rply[0] == 'null':
+			self.send_flags = AppleEvents.kAENoReply
+		else:
+			self.send_flags = AppleEvents.kAEWaitReply
+		self.send_priority = AppleEvents.kAENormalPriority	
+		if self.timeout:
+			self.send_timeout = self.timeout
+		else:
+			self.send_timeout = AppleEvents.kAEDefaultTimeout
+
+
+	def keyargs(self, ats, args):
+#		print 'keyargs', ats, args
+		output = {}
+		for arg in args.keys():
+			for at in ats:
+				if at[0] == arg:
+					output[at[1]] = self.keyfms(at[2][0], args[arg])
+		return output
+				
+	def keyfms(self, key, value):
+#		print 'keyfms', 'key', key, `value`
+		if key == 'obj ' or key == 'insl':
+			return eval(`value`)
+		elif key == 'TEXT':
+			return value
+		elif key == 'null':
+			return 
+		elif key == 'bool':
+			return baetypes.mkboolean(value)
+		elif key == 'type':
+			try:
+				val = eval('PsClass.%s()' % value)
+				return baetypes.mktype(str(val))
+			except:
+				return baetypes.mktype(value)
+		else:
+			print "I don't know what to put here -- script.keyargs"
+			print key, `value`
+			sys.exit[1]
+		
+	def newevent(self, code, subcode, parameters = {}, attributes = {}):
+		"""Create a complete structure for an apple event"""
+#		print code, subcode, parameters, attributes
+		event = AE.AECreateAppleEvent(code, subcode, self.target,
+			  	  AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
+		baetools.packevent(event, parameters, attributes)
+		return event
+		
+	def sendevent(self, event):
+		"""Send a pre-created appleevent, await the reply and unpack it"""
+		
+		reply = event.AESend(self.send_flags, self.send_priority,
+							  self.send_timeout)
+		parameters, attributes = baetools.unpackevent(reply)
+		return reply, parameters, attributes
+		
+	def send(self, code, subcode, parameters = {}, attributes = {}):
+		"""Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
+#		print code, subcode, parameters, attributes
+		return self.sendevent(self.newevent(code, subcode, parameters, attributes))
+		
+	def __str__(self):
+		_reply, _arguments, _attributes = self.send(self.code, self.subcode, self.arguments, self.attributes)
+
+		if _arguments.has_key('errn'):
+			raise baetools.Error, baetools.decodeerror(_arguments)
+		# XXXX Optionally decode result
+		if _arguments.has_key('----'):
+			return str(_arguments['----'])
+		else:
+			return 
+
+	
+	
+def test():
+	Simp = 'Hermit:Applications:SimpleText'
+	PsScript('MACS', Timeout=60*60*3)
+#	PsScript('CSOm', Timeout=60*60*3)
+#	PsScript('', Timeout=60*60*3)
+#	PyScript('macsoup')
+	ev = PsEvents
+	ps = PsClass
+#	print PsProperties.__dict__
+#	y = script(ev.Open, File('Hermit:Desktop Folder:Lincolnshire Imp'), using=Application_file(Simp))
+#	print baetypes.NProperty('prop', 'prop', 'pnam',  baetypes.ObjectSpecifier('cdis', 'indx', 1, None))
+#	y = PyScript(ev.Get, Disk("Hermit").Folder(7).File(1).Name())
+#	y = PyScript(ev.Get, Disk("Hermit").Size(), As='Integer')
+#	y = PyScript(ev.Get, ps.Desktopobject(1).Startup_disk())
+#	y = PyScript(ev.Get, Mailbox(1).File(), as='TEXT')
+#	print 'y', y, type(y)
+
+if __name__ == '__main__':
+	test()
+#	sys.exit(1)
+	
diff --git a/Mac/Contrib/PythonScript/ReadMe.txt b/Mac/Contrib/PythonScript/ReadMe.txt
new file mode 100644
index 0000000..3518423
--- /dev/null
+++ b/Mac/Contrib/PythonScript/ReadMe.txt
@@ -0,0 +1,86 @@
+
+PythonScript
+------------
+v0.5 beta 1 24/04/98
+
+author: Bill Bedford, <billb@mousa.demon.co.uk>
+
+This suite of modules is a first attempt at writing a more user friendly 
+python/appleevent interface. The files in the suite are:
+
+PythonScript
+------------
+
+Loads three dictionaries generated by getaete into __dict__'s of three 
+classes and thus gives us direct assess to all the methods in the aete.  
+Each method now contains all the information needed to build apple events.
+
+The general usage is
+
+>>>PythonScript.PsScript(SIGNATURE, TIMEOUT, IGNORING) 
+
+where 
+SIGNATURE is the target application
+TIMEOUT is in ticks
+and IGNORING is a boolean and determines whether the script waits for a reply 
+from the target before going on to the next event 
+
+>>>PythonScript.PyScript(Event, Object, keywdarg1..., keywdarg2...etc)
+
+Object is a appleevent object specifier and is of the form
+
+PythonScript.PsClass.Class1(arg).Class2(arg)Š.Property()
+
+All applescript event, class and property names are capitalised to 
+distinguish them from python methods.
+
+getaete
+-------
+
+Reads the aete of the target application and returns it as a list of three 
+dictionaries, which represent all the events, properties and enumeration in 
+the aete.  (the fourth dictionary, comparisons, has never been implemented 
+in applescript so I have not used it) It also reads the applescript aeut 
+and adds any suites that are missing (ie where the application author has 
+set his suite to inherit from the aeut.)  and the applescript suite, which 
+gives the language methods
+
+printaete
+---------
+
+Produces a text file with the aete set out in a human readable form, 
+similar to the Open Dictionary command in the applescript editor.
+
+
+baetools, baepack, baetypes
+---------------------------
+
+These are direct equivalents of aetools, aepack, aetypes in the standard 
+distribution.  Some methods and classes have been enhanced others are 
+redundant
+
+PyScriptTest, testeudora
+------------------------
+
+A couple of test scripts.  Testeudora is an updated version of the one in 
+the standard distribution.
+	 
+	 
+	 
+	 
+	 
+Still To Do (in no particular order)
+-----------
+
+These modules are much slower than applescript.  I believe they could be 
+made faster by rewriting the aete parser in getaete and/or by putting in 
+some form of persistent storage so that the class dictionaries can be cached.
+	 
+The parsing of the appleevent replies need rewriting.
+
+Support for the use of scripting additions.
+
+A Python aeut needs to be written, much of the applescript one is redundant 
+in python.
+
+Probably a few other things I haven't thought of yet.
diff --git a/Mac/Contrib/PythonScript/baepack.py b/Mac/Contrib/PythonScript/baepack.py
new file mode 100644
index 0000000..a0bbe50
--- /dev/null
+++ b/Mac/Contrib/PythonScript/baepack.py
@@ -0,0 +1,385 @@
+"""Tools for use in AppleEvent clients and servers:
+conversion between AE types and python types
+
+pack(x) converts a Python object to an AEDesc object
+unpack(desc) does the reverse
+coerce(x, wanted_sample) coerces a python object to another python object
+"""
+
+#
+# This code was originally written by Guido, and modified/extended by Jack
+# to include the various types that were missing. The reference used is
+# Apple Event Registry, chapter 9.
+#
+
+import struct
+import string
+import types
+from string import strip
+from types import *
+import AE
+from AppleEvents import *
+from AERegistry import *
+from AEObjects import *
+import MacOS
+import macfs
+import StringIO
+import baetypes
+from baetypes import mkenum, mktype
+
+import calldll
+
+OSL = calldll.getlibrary('ObjectSupportLib')
+
+# These ones seem to be missing from AppleEvents
+# (they're in AERegistry.h)
+
+#typeColorTable = 'clrt'
+#typeDrawingArea = 'cdrw'
+#typePixelMap = 'cpix'
+#typePixelMapMinus = 'tpmm'
+#typeRotation = 'trot'
+#typeTextStyles = 'tsty'
+#typeStyledText = 'STXT'
+#typeAEText = 'tTXT'
+#typeEnumeration = 'enum'
+
+#
+# Some AE types are immedeately coerced into something
+# we like better (and which is equivalent)
+#
+unpacker_coercions = {
+	typeComp : typeExtended,
+	typeColorTable : typeAEList,
+	typeDrawingArea : typeAERecord,
+	typeFixed : typeExtended,
+	typeFloat : typeExtended,
+	typePixelMap : typeAERecord,
+	typeRotation : typeAERecord,
+	typeStyledText : typeAERecord,
+	typeTextStyles : typeAERecord,
+};
+
+#
+# Some python types we need in the packer:
+#
+AEDescType = type(AE.AECreateDesc('TEXT', ''))
+_sample_fss = macfs.FSSpec(':')
+_sample_alias = _sample_fss.NewAliasMinimal()
+FSSType = type(_sample_fss)
+AliasType = type(_sample_alias)
+
+def pack(x, forcetype = None):
+	"""Pack a python object into an AE descriptor"""
+#	print 'aepack', x, type(x), forcetype
+#	if type(x) == TupleType:
+#		forcetype, x = x
+	if forcetype:
+		print x, forcetype
+		if type(x) is StringType:
+			return AE.AECreateDesc(forcetype, x)
+		else:
+			return pack(x).AECoerceDesc(forcetype)
+			
+	if x == None:
+		return AE.AECreateDesc('null', '')
+		
+	t = type(x)
+	if t == AEDescType:
+		return x
+	if t == FSSType:
+		return AE.AECreateDesc('fss ', x.data)
+	if t == AliasType:
+		return AE.AECreateDesc('alis', x.data)
+	if t == IntType:
+		return AE.AECreateDesc('long', struct.pack('l', x))
+	if t == FloatType:
+		#
+		# XXXX (note by Guido) Weird thing -- Think C's "double" is 10 bytes, but
+		# struct.pack('d') return 12 bytes (and struct.unpack requires
+		# them, too).  The first 2 bytes seem to be repeated...
+		# Probably an alignment problem
+		# XXXX (note by Jack) haven't checked this under MW
+		#
+#		return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
+		return AE.AECreateDesc('exte', struct.pack('d', x))
+	if t == StringType:
+		return AE.AECreateDesc('TEXT', x)
+	if t == ListType:
+		list = AE.AECreateList('', 0)
+		for item in x:
+			list.AEPutDesc(0, pack(item))
+		return list
+	if t == DictionaryType:
+		record = AE.AECreateList('', 1)
+		for key, value in x.items():
+			record.AEPutParamDesc(key, pack(value))
+		return record
+	if t == InstanceType and hasattr(x, '__aepack__'):
+		return x.__aepack__()
+	return AE.AECreateDesc('TEXT', repr(x)) # Copout
+
+def unpack(desc):
+	"""Unpack an AE descriptor to a python object"""
+	t = desc.type
+#	print t
+	
+	if unpacker_coercions.has_key(t):
+		desc = desc.AECoerceDesc(unpacker_coercions[t])
+		t = desc.type # This is a guess by Jack....
+	
+	if t == typeAEList:
+		l = []
+		for i in range(desc.AECountItems()):
+			keyword, item = desc.AEGetNthDesc(i+1, '****')
+			l.append(unpack(item))
+		return l
+	if t == typeAERecord:
+		d = {}
+		for i in range(desc.AECountItems()):
+			keyword, item = desc.AEGetNthDesc(i+1, '****')
+			d[keyword] = unpack(item)
+		return d
+	if t == typeAEText:
+		record = desc.AECoerceDesc('reco')
+		return mkaetext(unpack(record))
+	if t == typeAlias:
+		return macfs.RawAlias(desc.data)
+	# typeAppleEvent returned as unknown
+	if t == typeBoolean:
+		return struct.unpack('b', desc.data)[0]
+	if t == typeChar:
+		return desc.data
+	# typeColorTable coerced to typeAEList
+	# typeComp coerced to extended
+	# typeData returned as unknown
+	# typeDrawingArea coerced to typeAERecord
+	if t == typeEnumeration:
+		return mkenum(desc.data)
+	# typeEPS returned as unknown
+	if t == typeExtended:
+#		print desc, type(desc), len(desc)
+		data = desc.data
+#		print `data[:8]`, type(data), len(data[:8])
+#		print struct.unpack('=d', data[:8])[0]
+#		print string.atoi(data), type(data), len(data)
+#		print struct.calcsize(data)
+		# XXX See corresponding note for pack()
+#		return struct.unpack('d', data[:2] + data)[0]
+		return struct.unpack('d', data[:8])[0]
+	if t == typeFalse:
+		return 0
+	# typeFixed coerced to extended
+	# typeFloat coerced to extended
+	if t == typeFSS:
+		return macfs.RawFSSpec(desc.data)
+	if t == typeInsertionLoc:
+		record = desc.AECoerceDesc('reco')
+		return mkinsertionloc(unpack(record))
+	# typeInteger equal to typeLongInteger
+	if t == typeIntlText:
+		script, language = struct.unpack('hh', desc.data[:4])
+		return baetypes.IntlText(script, language, desc.data[4:])
+	if t == typeIntlWritingCode:
+		script, language = struct.unpack('hh', desc.data)
+		return baetypes.IntlWritingCode(script, language)
+	if t == typeKeyword:
+		return mkkeyword(desc.data)
+	# typeLongFloat is equal to typeFloat
+	if t == typeLongInteger:
+#		print t, struct.unpack('l', desc.data)
+		return struct.unpack('l', desc.data)[0]
+	if t == typeNull:
+		return None
+	if t == typeMagnitude:
+		v = struct.unpack('l', desc.data)
+		if v < 0:
+			v = 0x100000000L + v
+		return v
+	if t == typeObjectSpecifier:
+		import Res
+#		print desc, type(desc)
+#		print desc.__members__
+#		print desc.data, desc.type
+#		print unpack(desc)
+#		getOSL = calldll.newcall(OSL.AEResolve, 'OSErr', 'InHandle', 'InShort')#, 'InString')
+#		print 'OSL', getOSL(rdesc, 0)#, desc.data)
+		record = desc.AECoerceDesc('reco')
+#		print record
+		return mkobject(unpack(record))
+	# typePict returned as unknown
+	# typePixelMap coerced to typeAERecord
+	# typePixelMapMinus returned as unknown
+	# typeProcessSerialNumber returned as unknown
+	if t == typeQDPoint:
+		v, h = struct.unpack('hh', desc.data)
+		return baetypes.QDPoint(v, h)
+	if t == typeQDRectangle:
+		v0, h0, v1, h1 = struct.unpack('hhhh', desc.data)
+		return baetypes.QDRectangle(v0, h0, v1, h1)
+	if t == typeRGBColor:
+		r, g, b = struct.unpack('hhh', desc.data)
+		return baetypes.RGBColor(r, g, b)
+	# typeRotation coerced to typeAERecord
+	# typeScrapStyles returned as unknown
+	# typeSessionID returned as unknown
+	if t == typeShortFloat:
+		return struct.unpack('f', desc.data)[0]
+	if t == typeShortInteger:
+#		print t, desc.data
+#		print struct.unpack('h', desc.data)[0]
+		return struct.unpack('h', desc.data)[0]
+	# typeSMFloat identical to typeShortFloat
+	# typeSMInt	indetical to typeShortInt
+	# typeStyledText coerced to typeAERecord
+	if t == typeTargetID:
+		return mktargetid(desc.data)
+	# typeTextStyles coerced to typeAERecord
+	# typeTIFF returned as unknown
+	if t == typeTrue:
+		return 1
+	if t == typeType:
+#		print t, desc.data
+		return mktype(desc.data)
+	#
+	# The following are special
+	#
+	if t == 'rang':
+		record = desc.AECoerceDesc('reco')
+		return mkrange(unpack(record))
+	if t == 'cmpd':
+		record = desc.AECoerceDesc('reco')
+		return mkcomparison(unpack(record))
+	if t == 'logi':
+		record = desc.AECoerceDesc('reco')
+		return mklogical(unpack(record))
+	return mkunknown(desc.type, desc.data)
+	
+def coerce(data, egdata):
+	"""Coerce a python object to another type using the AE coercers"""
+	pdata = pack(data)
+	pegdata = pack(egdata)
+	pdata = pdata.AECoerceDesc(pegdata.type)
+	return unpack(pdata)
+
+#
+# Helper routines for unpack
+#
+def mktargetid(data):
+	sessionID = getlong(data[:4])
+	name = mkppcportrec(data[4:4+72])
+	location = mklocationnamerec(data[76:76+36])
+	rcvrName = mkppcportrec(data[112:112+72])
+	return sessionID, name, location, rcvrName
+
+def mkppcportrec(rec):
+	namescript = getword(rec[:2])
+	name = getpstr(rec[2:2+33])
+	portkind = getword(rec[36:38])
+	if portkind == 1:
+		ctor = rec[38:42]
+		type = rec[42:46]
+		identity = (ctor, type)
+	else:
+		identity = getpstr(rec[38:38+33])
+	return namescript, name, portkind, identity
+
+def mklocationnamerec(rec):
+	kind = getword(rec[:2])
+	stuff = rec[2:]
+	if kind == 0: stuff = None
+	if kind == 2: stuff = getpstr(stuff)
+	return kind, stuff
+
+def mkunknown(type, data):
+	return baetypes.Unknown(type, data)
+
+def getpstr(s):
+	return s[1:1+ord(s[0])]
+
+def getlong(s):
+	return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
+
+def getword(s):
+	return (ord(s[0])<<8) | (ord(s[1])<<0)
+
+def mkkeyword(keyword):
+	return baetypes.Keyword(keyword)
+
+def mkrange(dict):
+	return baetypes.Range(dict['star'], dict['stop'])
+
+def mkcomparison(dict):
+	return baetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
+
+def mklogical(dict):
+	return baetypes.Logical(dict['logc'], dict['term'])
+
+def mkstyledtext(dict):
+	return baetypes.StyledText(dict['ksty'], dict['ktxt'])
+	
+def mkaetext(dict):
+	return baetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText])
+	
+def mkinsertionloc(dict):
+	return baetypes.InsertionLoc(dict[keyAEObject], dict[keyAEPosition])
+
+def mkobject(dict):
+	want = dict['want'].type
+	form = dict['form'].enum
+	seld = dict['seld']
+	fr   = dict['from']
+	if form in ('name', 'indx', 'rang', 'test'):
+		if want == 'text': return baetypes.Text(seld, fr)
+		if want == 'cha ': return baetypes.Character(seld, fr)
+		if want == 'cwor': return baetypes.Word(seld, fr)
+		if want == 'clin': return baetypes.Line(seld, fr)
+		if want == 'cpar': return baetypes.Paragraph(seld, fr)
+		if want == 'cwin': return baetypes.Window(seld, fr)
+		if want == 'docu': return baetypes.Document(seld, fr)
+		if want == 'file': return baetypes.File(seld, fr)
+		if want == 'cins': return baetypes.InsertionPoint(seld, fr)
+	if want == 'prop' and form == 'prop' and baetypes.IsType(seld):
+		return baetypes.Property(seld.type, fr)
+	return baetypes.ObjectSpecifier(want, form, seld, fr)
+
+def _test():
+	"""Test program. Pack and unpack various things"""
+	objs = [
+		'a string',
+		12,
+		12.0,
+		None,
+		['a', 'list', 'of', 'strings'],
+		{'key1': 'value1', 'key2':'value2'},
+		macfs.FSSpec(':'),
+		macfs.FSSpec(':').NewAliasMinimal(),
+		baetypes.Enum('enum'),
+		baetypes.Type('type'),
+		baetypes.Keyword('kwrd'),
+		baetypes.Range(1, 10),
+		baetypes.Comparison(1, '<   ', 10),
+		baetypes.Logical('not ', 1),
+		# Cannot do StyledText
+		# Cannot do AEText
+		baetypes.IntlText(0, 0, 'international text'),
+		baetypes.IntlWritingCode(0,0),
+		baetypes.QDPoint(50,100),
+		baetypes.QDRectangle(50,100,150,200),
+		baetypes.RGBColor(0x7000, 0x6000, 0x5000),
+		baetypes.Unknown('xxxx', 'unknown type data'),
+		baetypes.Character(1),
+		baetypes.Character(2, baetypes.Line(2)),
+	]
+	for o in objs:
+		print 'BEFORE', o, `o`
+		print type(o)
+		packed = pack(o)
+		unpacked = unpack(packed)
+		print 'AFTER ', unpacked, `unpacked`
+	import sys
+	sys.exit(1)
+	
+if __name__ == '__main__':
+	_test()
+	
diff --git a/Mac/Contrib/PythonScript/baetools.py b/Mac/Contrib/PythonScript/baetools.py
new file mode 100644
index 0000000..8f0bb04
--- /dev/null
+++ b/Mac/Contrib/PythonScript/baetools.py
@@ -0,0 +1,263 @@
+"""Tools for use in AppleEvent clients and servers.
+
+pack(x) converts a Python object to an AEDesc object
+unpack(desc) does the reverse
+
+packevent(event, parameters, attributes) sets params and attrs in an AEAppleEvent record
+unpackevent(event) returns the parameters and attributes from an AEAppleEvent record
+
+Plus...  Lots of classes and routines that help representing AE objects,
+ranges, conditionals, logicals, etc., so you can write, e.g.:
+
+	x = Character(1, Document("foobar"))
+
+and pack(x) will create an AE object reference equivalent to AppleScript's
+
+	character 1 of document "foobar"
+
+Some of the stuff that appears to be exported from this module comes from other
+files: the pack stuff from aepack, the objects from aetypes.
+
+"""
+
+from types import *
+import AE
+import AppleEvents
+import MacOS
+import sys
+
+from baetypes import *
+from baepack import pack, unpack, coerce, AEDescType
+
+Error = 'baetools.Error'
+
+# Special code to unpack an AppleEvent (which is *not* a disguised record!)
+# Note by Jack: No??!? If I read the docs correctly it *is*....
+
+aekeywords = [
+	'tran',
+	'rtid',
+	'evcl',
+	'evid',
+	'addr',
+	'optk',
+	'timo',
+	'inte',	# this attribute is read only - will be set in AESend
+	'esrc',	# this attribute is read only
+	'miss',	# this attribute is read only
+	'from'	# new in 1.0.1
+]
+
+def missed(ae):
+	try:
+		desc = ae.AEGetAttributeDesc('miss', 'keyw')
+	except AE.Error, msg:
+		return None
+	return desc.data
+
+def unpackevent(ae):
+	parameters = {}
+	while 1:
+		key = missed(ae)
+		if not key: break
+		parameters[key] = unpack(ae.AEGetParamDesc(key, '****'))
+	attributes = {}
+	for key in aekeywords:
+		try:
+			desc = ae.AEGetAttributeDesc(key, '****')
+		except (AE.Error, MacOS.Error), msg:
+			if msg[0] != -1701 and msg[0] != -1704:
+				raise sys.exc_type, sys.exc_value
+			continue
+		attributes[key] = unpack(desc)
+	return parameters, attributes
+
+def packevent(ae, parameters = {}, attributes = {}):
+	for key, value in parameters.items():
+		ae.AEPutParamDesc(key, pack(value))
+	for key, value in attributes.items():
+		ae.AEPutAttributeDesc(key, pack(value))
+
+#
+# Support routine for automatically generated Suite interfaces
+# These routines are also useable for the reverse function.
+#
+def keysubst(arguments, keydict):
+	"""Replace long name keys by their 4-char counterparts, and check"""
+	ok = keydict.values()
+	for k in arguments.keys():
+		if keydict.has_key(k):
+			v = arguments[k]
+			del arguments[k]
+			arguments[keydict[k]] = v
+		elif k != '----' and k not in ok:
+			raise TypeError, 'Unknown keyword argument: %s'%k
+			
+def enumsubst(arguments, key, edict):
+	"""Substitute a single enum keyword argument, if it occurs"""
+	if not arguments.has_key(key):
+		return
+	v = arguments[key]
+	ok = edict.values()
+	if edict.has_key(v):
+		arguments[key] = edict[v]
+	elif not v in ok:
+		raise TypeError, 'Unknown enumerator: %s'%v
+		
+def decodeerror(arguments):
+	"""Create the 'best' argument for a raise MacOS.Error"""
+	errn = arguments['errn']
+	err_a1 = errn
+	if arguments.has_key('errs'):
+		err_a2 = arguments['errs']
+	else:
+		err_a2 = MacOS.GetErrorString(errn)
+	if arguments.has_key('erob'):
+		err_a3 = arguments['erob']
+	else:
+		err_a3 = None
+	
+	return (err_a1, err_a2, err_a3)
+
+class TalkTo:
+	"""An AE connection to an application"""
+	
+	def __init__(self, signature, start=0, timeout=0):
+		"""Create a communication channel with a particular application.		
+		Addressing the application is done by specifying either a
+		4-byte signature, an AEDesc or an object that will __aepack__
+		to an AEDesc.
+		"""
+		self.target_signature = None
+		if type(signature) == AEDescType:
+			self.target = signature
+		elif type(signature) == InstanceType and hasattr(signature, '__aepack__'):
+			self.target = signature.__aepack__()
+		elif type(signature) == StringType and len(signature) == 4:
+			self.target = AE.AECreateDesc(AppleEvents.typeApplSignature, signature)
+			self.target_signature = signature
+		else:
+			raise TypeError, "signature should be 4-char string or AEDesc"
+		self.send_flags = AppleEvents.kAEWaitReply
+		self.send_priority = AppleEvents.kAENormalPriority
+		if timeout:
+			self.send_timeout = timeout
+		else:
+			self.send_timeout = AppleEvents.kAEDefaultTimeout
+		if start:
+			self.start()
+		
+	def start(self):
+		"""Start the application, if it is not running yet"""
+		self.send_flags = AppleEvents.kAENoReply
+		_launch(self.target_signature)
+			
+	def newevent(self, code, subcode, parameters = {}, attributes = {}):
+		"""Create a complete structure for an apple event"""
+		event = AE.AECreateAppleEvent(code, subcode, self.target,
+		      	  AppleEvents.kAutoGenerateReturnID, AppleEvents.kAnyTransactionID)
+#		print parameters, attributes
+		packevent(event, parameters, attributes)
+		return event
+	
+	def sendevent(self, event):
+		"""Send a pre-created appleevent, await the reply and unpack it"""
+		
+		reply = event.AESend(self.send_flags, self.send_priority,
+		                          self.send_timeout)
+		parameters, attributes = unpackevent(reply)
+		return reply, parameters, attributes
+		
+	def send(self, code, subcode, parameters = {}, attributes = {}):
+		"""Send an appleevent given code/subcode/pars/attrs and unpack the reply"""
+		return self.sendevent(self.newevent(code, subcode, parameters, attributes))
+	
+	#
+	# The following events are somehow "standard" and don't seem to appear in any
+	# suite...
+	#
+	def activate(self):
+		"""Send 'activate' command"""
+		self.send('misc', 'actv')
+
+	def _get(self, _object, as=None, _attributes={}):
+		"""_get: get data from an object
+		Required argument: the object
+		Keyword argument _attributes: AppleEvent attribute dictionary
+		Returns: the data
+		"""
+		_code = 'core'
+		_subcode = 'getd'
+
+		_arguments = {'----':_object}
+		if as:
+			_arguments['rtyp'] = mktype(as)
+
+		_reply, _arguments, _attributes = self.send(_code, _subcode,
+				_arguments, _attributes)
+		if _arguments.has_key('errn'):
+			raise Error, decodeerror(_arguments)
+
+		if _arguments.has_key('----'):
+			return _arguments['----']
+
+# Tiny Finder class, for local use only
+
+class _miniFinder(TalkTo):
+	def open(self, _object, _attributes={}, **_arguments):
+		"""open: Open the specified object(s)
+		Required argument: list of objects to open
+		Keyword argument _attributes: AppleEvent attribute dictionary
+		"""
+		_code = 'aevt'
+		_subcode = 'odoc'
+
+		if _arguments: raise TypeError, 'No optional args expected'
+		_arguments['----'] = _object
+
+
+		_reply, _arguments, _attributes = self.send(_code, _subcode,
+				_arguments, _attributes)
+		if _arguments.has_key('errn'):
+			raise aetools.Error, aetools.decodeerror(_arguments)
+		# XXXX Optionally decode result
+		if _arguments.has_key('----'):
+			return _arguments['----']
+#pass
+	
+_finder = _miniFinder('MACS')
+
+def _launch(appfile):
+	"""Open a file thru the finder. Specify file by name or fsspec"""
+	_finder.open(_application_file(('ID  ', appfile)))
+
+
+class _application_file(ComponentItem):
+	"""application file - An application's file on disk"""
+	want = 'appf'
+	
+_application_file._propdict = {
+}
+_application_file._elemdict = {
+}
+	
+# Test program
+# XXXX Should test more, really...
+
+def test():
+	target = AE.AECreateDesc('sign', 'quil')
+	ae = AE.AECreateAppleEvent('aevt', 'oapp', target, -1, 0)
+	print unpackevent(ae)
+	raw_input(":")
+	ae = AE.AECreateAppleEvent('core', 'getd', target, -1, 0)
+	obj = Character(2, Word(1, Document(1)))
+	print obj
+	print repr(obj)
+	packevent(ae, {'----': obj})
+	params, attrs = unpackevent(ae)
+	print params['----']
+	raw_input(":")
+
+if __name__ == '__main__':
+	test()
+	sys.exit(1)
diff --git a/Mac/Contrib/PythonScript/baetypes.py b/Mac/Contrib/PythonScript/baetypes.py
new file mode 100644
index 0000000..b087548
--- /dev/null
+++ b/Mac/Contrib/PythonScript/baetypes.py
@@ -0,0 +1,564 @@
+"""aetypes - Python objects representing various AE types."""
+
+from AppleEvents import *
+from AERegistry import *
+from AEObjects import *
+import struct
+from types import *
+import string
+
+#
+# convoluted, since there are cyclic dependencies between this file and
+# aetools_convert.
+#
+def pack(*args):
+	from aepack import pack
+	return apply(pack, args)
+	
+def IsSubclass(cls, base):
+	"""Test whether CLASS1 is the same as or a subclass of CLASS2"""
+	# Loop to optimize for single inheritance
+	while 1:
+		if cls is base: return 1
+		if len(cls.__bases__) <> 1: break
+		cls = cls.__bases__[0]
+	# Recurse to cope with multiple inheritance
+	for c in cls.__bases__:
+		if IsSubclass(c, base): return 1
+	return 0
+
+def IsInstance(x, cls):
+	"""Test whether OBJECT is an instance of (a subclass of) CLASS"""
+	return type(x) is InstanceType and IsSubclass(x.__class__, cls)
+
+def nice(s):
+	"""'nice' representation of an object"""
+	if type(s) is StringType: return repr(s)
+	else: return str(s)
+
+class Unknown:
+	"""An uninterpreted AE object"""
+	
+	def __init__(self, type, data):
+		self.type = type
+		self.data = data
+	
+	def __repr__(self):
+		return "Unknown(%s, %s)" % (`self.type`, `self.data`)
+	
+	def __aepack__(self):
+		return pack(self.data, self.type)
+
+class Enum:
+	"""An AE enumeration value"""
+	
+	def __init__(self, enum):
+		self.enum = "%-4.4s" % str(enum)
+	
+	def __repr__(self):
+		return "Enum(%s)" % `self.enum`
+	
+	def __str__(self):
+		return string.strip(self.enum)
+	
+	def __aepack__(self):
+		return pack(self.enum, typeEnumeration)
+
+def IsEnum(x):
+	return IsInstance(x, Enum)
+
+def mkenum(enum):
+#	print enum
+	if IsEnum(enum): return enum
+	return Enum(enum)
+
+class Boolean:
+	"""An AE boolean value"""
+	
+	def __init__(self, bool):
+		if bool:
+			self.bool = "%-4.4s" % str(typeTrue)
+		else:
+			self.bool = "%-4.4s" % str(typeFalse)
+	
+	def __repr__(self):
+		return "Boolean(%s)" % self.bool
+	
+	def __str__(self):
+		return self.bool
+	
+	def __aepack__(self):
+		if self.bool == 'true':
+			return pack('', typeTrue)
+		else:
+			return pack('', typeFalse)
+
+def IsBoolean(x):
+	return IsInstance(x, Boolean)
+
+def mkboolean(bool):
+#	print bool
+	if IsBoolean(bool): return bool
+	return Boolean(bool)
+
+class Type:
+	"""An AE 4-char typename object"""
+	
+	def __init__(self, _type):
+		self.type = "%-4.4s" % str(_type)
+		
+	def __repr__(self):
+		return "Type(%s)" % `self.type`
+	
+	def __str__(self):
+		return string.strip(self.type)
+	
+	def __aepack__(self):
+#		print self.type, typeType
+		return pack(self.type, typeType)
+
+def IsType(x):
+	return IsInstance(x, Type)
+
+def mktype(_type):
+	# Should check for apple ID codes, will allow 
+	if IsType(_type): return _type
+	if type(_type) <> StringType: return _type
+	if len(_type) <> 4: return Type(eval('type' + _type))
+	return Type(_type)
+
+
+class Keyword:
+	"""An AE 4-char keyword object"""
+	
+	def __init__(self, keyword):
+		self.keyword = "%-4.4s" % str(keyword)
+	
+	def __repr__(self):
+		return "Keyword(%s)" % `self.keyword`
+	
+	def __str__(self):
+		return string.strip(self.keyword)
+	
+	def __aepack__(self):
+		return pack(self.keyword, typeKeyword)
+
+def IsKeyword(x):
+	return IsInstance(x, Keyword)
+
+class Range:
+	"""An AE range object"""
+	
+	def __init__(self, start, stop):
+		self.start = start
+		self.stop = stop
+	
+	def __repr__(self):
+		return "Range(%s, %s)" % (`self.start`, `self.stop`)
+	
+	def __str__(self):
+		return "%s thru %s" % (nice(self.start), nice(self.stop))
+	
+	def __aepack__(self):
+		return pack({'star': self.start, 'stop': self.stop}, 'rang')
+
+def IsRange(x):
+	return IsInstance(x, Range)
+
+class Comparison:
+	"""An AE Comparison"""
+	
+	def __init__(self, obj1, relo, obj2):
+		self.obj1 = obj1
+		self.relo = "%-4.4s" % str(relo)
+		self.obj2 = obj2
+	
+	def __repr__(self):
+		return "Comparison(%s, %s, %s)" % (`self.obj1`, `self.relo`, `self.obj2`)
+	
+	def __str__(self):
+		return "%s %s %s" % (nice(self.obj1), string.strip(self.relo), nice(self.obj2))
+	
+	def __aepack__(self):
+		return pack({'obj1': self.obj1,
+			     'relo': mkenum(self.relo),
+			     'obj2': self.obj2},
+			    'cmpd')
+
+def IsComparison(x):
+	return IsInstance(x, Comparison)
+	
+class NComparison(Comparison):
+	# The class attribute 'relo' must be set in a subclass
+	
+	def __init__(self, obj1, obj2):
+		Comparison.__init__(obj1, self.relo, obj2)
+
+class Ordinal:
+	"""An AE Ordinal"""
+	
+	def __init__(self, ord):
+		self.ord = ord
+	
+	def __repr__(self):
+		return  "baetypes.Ordinal(%s)" % `self.ord`
+	
+	def __str__(self):
+		return "%s" % (string.strip(self.ord))
+	
+	def __aepack__(self):
+		return pack(self.ord, typeAbsoluteOrdinal)
+
+def IsOrdinal(x):
+#	print 'IsOrdinal', x, IsInstance(x, Ordinal)
+	return IsInstance(x, Ordinal)
+	
+def mkOrdinal(Ord):
+	if IsOrdinal(Ord): return Ord
+	return Ordinal(Ord)
+
+
+	
+class NOrdinal(Ordinal):
+	# The class attribute 'abso' must be set in a subclass
+	
+	def __init__(self ):
+#		print 'NOrdinal', self.abso
+		Ordinal.__init__(self, self.abso)
+
+class Logical:
+	"""An AE logical expression object"""
+	
+	def __init__(self, logc, term):
+		self.logc = "%-4.4s" % str(logc)
+		self.term = term
+	
+	def __repr__(self):
+		return "Logical(%s, %s)" % (`self.logc`, `self.term`)
+	
+	def __str__(self):
+		if type(self.term) == ListType and len(self.term) == 2:
+			return "%s %s %s" % (nice(self.term[0]),
+			                     string.strip(self.logc),
+			                     nice(self.term[1]))
+		else:
+			return "%s(%s)" % (string.strip(self.logc), nice(self.term))
+	
+	def __aepack__(self):
+		return pack({'logc': mkenum(self.logc), 'term': self.term}, 'logi')
+
+def IsLogical(x):
+	return IsInstance(x, Logical)
+
+class StyledText:
+	"""An AE object respresenting text in a certain style"""
+	
+	def __init__(self, style, text):
+		self.style = style
+		self.text = text
+	
+	def __repr__(self):
+		return "StyledText(%s, %s)" % (`self.style`, `self.text`)
+	
+	def __str__(self):
+		return self.text
+	
+	def __aepack__(self):
+		return pack({'ksty': self.style, 'ktxt': self.text}, 'STXT')
+
+def IsStyledText(x):
+	return IsInstance(x, StyledText)
+
+class AEText:
+	"""An AE text object with style, script and language specified"""
+	
+	def __init__(self, script, style, text):
+		self.script = script
+		self.style = style
+		self.text = text
+	
+	def __repr__(self):
+		return "AEText(%s, %s, %s)" % (`self.script`, `self.style`, `self.text`)
+	
+	def __str__(self):
+		return self.text
+	
+	def __aepack__(self):
+		return pack({keyAEScriptTag: self.script, keyAEStyles: self.style,
+				 keyAEText: self.text}, typeAEText)
+
+def IsAEText(x):
+	return IsInstance(x, AEText)
+
+class IntlText:
+	"""A text object with script and language specified"""
+	
+	def __init__(self, script, language, text):
+		self.script = script
+		self.language = language
+		self.text = text
+	
+	def __repr__(self):
+		return "IntlText(%s, %s, %s)" % (`self.script`, `self.language`, `self.text`)
+	
+	def __str__(self):
+		return self.text
+	
+	def __aepack__(self):
+		return pack(struct.pack('hh', self.script, self.language)+self.text,
+			typeIntlText)
+
+def IsIntlText(x):
+	return IsInstance(x, IntlText)
+
+class IntlWritingCode:
+	"""An object representing script and language"""
+	
+	def __init__(self, script, language):
+		self.script = script
+		self.language = language
+	
+	def __repr__(self):
+		return "IntlWritingCode(%s, %s)" % (`self.script`, `self.language`)
+	
+	def __str__(self):
+		return "script system %d, language %d"%(self.script, self.language)
+	
+	def __aepack__(self):
+		return pack(struct.pack('hh', self.script, self.language),
+			typeIntlWritingCode)
+
+def IsIntlWritingCode(x):
+	return IsInstance(x, IntlWritingCode)
+
+class QDPoint:
+	"""A point"""
+	
+	def __init__(self, v, h):
+		self.v = v
+		self.h = h
+	
+	def __repr__(self):
+		return "QDPoint(%s, %s)" % (`self.v`, `self.h`)
+	
+	def __str__(self):
+		return "(%d, %d)"%(self.v, self.h)
+	
+	def __aepack__(self):
+		return pack(struct.pack('hh', self.v, self.h),
+			typeQDPoint)
+
+def IsQDPoint(x):
+	return IsInstance(x, QDPoint)
+
+class QDRectangle:
+	"""A rectangle"""
+	
+	def __init__(self, v0, h0, v1, h1):
+		self.v0 = v0
+		self.h0 = h0
+		self.v1 = v1
+		self.h1 = h1
+	
+	def __repr__(self):
+		return "QDRectangle(%s, %s, %s, %s)" % (`self.v0`, `self.h0`,
+				`self.v1`, `self.h1`)
+	
+	def __str__(self):
+		return "(%d, %d)-(%d, %d)"%(self.v0, self.h0, self.v1, self.h1)
+	
+	def __aepack__(self):
+		return pack(struct.pack('hhhh', self.v0, self.h0, self.v1, self.h1),
+			typeQDRectangle)
+
+def IsQDRectangle(x):
+	return IsInstance(x, QDRectangle)
+
+class RGBColor:
+	"""An RGB color"""
+	
+	def __init__(self, r, g, b):
+		self.r = r
+		self.g = g
+		self.b = b
+			
+	def __repr__(self):
+		return "RGBColor(%s, %s, %s)" % (`self.r`, `self.g`, `self.b`)
+	
+	def __str__(self):
+		return "0x%x red, 0x%x green, 0x%x blue"% (self.r, self.g, self.b)
+	
+	def __aepack__(self):
+		return pack(struct.pack('hhh', self.r, self.g, self.b),
+			typeRGBColor)
+
+def IsRGBColor(x):
+	return IsInstance(x, RGBColor)
+
+class ObjectSpecifier:
+	
+	"""A class for constructing and manipulation AE object specifiers in python.
+	
+	An object specifier is actually a record with four fields:
+	
+	key	type	description
+	---	----	-----------
+	
+	'want'	type	4-char class code of thing we want,
+			e.g. word, paragraph or property
+	
+	'form'	enum	how we specify which 'want' thing(s) we want,
+			e.g. by index, by range, by name, or by property specifier
+	
+	'seld'	any	which thing(s) we want,
+			e.g. its index, its name, or its property specifier
+	
+	'from'	object	the object in which it is contained,
+			or null, meaning look for it in the application
+	
+	Note that we don't call this class plain "Object", since that name
+	is likely to be used by the application.
+	"""
+	
+	def __init__(self, want, form, seld, fr = None):
+		self.want = want
+		self.form = form
+		self.seld = seld
+		self.fr = fr
+	
+	def __repr__(self):
+		s = "ObjectSpecifier(%s, %s, %s" % (`self.want`, `self.form`, `self.seld`)
+		if self.fr:
+			s = s + ", %s)" % `self.fr`
+		else:
+			s = s + ")"
+		return s
+	
+	def __aepack__(self):
+		return pack({'want': mktype(self.want),
+			     'form': mkenum(self.form),
+			     'seld': self.seld,
+			     'from': self.fr},
+			    'obj ')
+
+def IsObjectSpecifier(x):
+	return IsInstance(x, ObjectSpecifier)
+
+
+# Backwards compatability, sigh...
+class Property(ObjectSpecifier):
+
+	def __init__(self, which, fr = None, want='prop'):
+		ObjectSpecifier.__init__(self, want, 'prop', mktype(which), fr)
+
+	def __repr__(self):
+		if self.fr:
+			return "Property_r(%s, %s)" % (`self.seld.type`, `self.fr`)
+		else:
+			return "Property_r(%s)" % `self.seld.type`
+	
+	def __str__(self):
+		if self.fr:
+			return "Property %s of %s" % (str(self.seld), str(self.fr))
+		else:
+			return "Property_s %s" % str(self.seld)
+
+
+class NProperty(ObjectSpecifier):
+	# Subclasses *must* self baseclass attributes:
+	# want is the type of this property
+	# which is the property name of this property
+
+	def __init__(self, want, form, seld, fr = None):
+		ObjectSpecifier.__init__(self, want, form, 
+					mktype(seld), fr)
+
+
+class SelectableItem(ObjectSpecifier):
+	
+	def __init__(self, want, seld, fr = None):
+		t = type(seld)
+		if t == StringType:
+			form = 'name'
+		elif IsRange(seld):
+			form = 'rang'
+		elif IsComparison(seld) or IsLogical(seld):
+			form = 'test'
+		elif t == TupleType:
+			# Breakout: specify both form and seld in a tuple
+			# (if you want ID or rele or somesuch)
+			form, seld = seld
+		else:
+			form = 'indx'
+		ObjectSpecifier.__init__(self, want, form, seld, fr)
+
+
+class ComponentItem(SelectableItem):
+	# Derived classes *must* set the *class attribute* 'want' to some constant
+	# Also, dictionaries _propdict and _elemdict must be set to map property
+	# and element names to the correct classes
+	
+	def __init__(self, want, which, fr = None):
+		SelectableItem.__init__(self, want, which, fr)
+	
+	def __repr__(self):
+		if not self.fr:
+			return "%s(%s)" % (self.__class__.__name__, `self.seld`)
+		return "%s(%s, %s)" % (self.__class__.__name__, `self.seld`, `self.fr`)
+	
+	def __str__(self):
+		seld = self.seld
+		if type(seld) == StringType:
+			ss = repr(seld)
+		elif IsRange(seld):
+			start, stop = seld.start, seld.stop
+			if type(start) == InstanceType == type(stop) and \
+			   start.__class__ == self.__class__ == stop.__class__:
+				ss = str(start.seld) + " thru " + str(stop.seld)
+			else:
+				ss = str(seld)
+		else:
+			ss = str(seld)
+		s = "%s %s" % (self.__class__.__name__, ss)
+		if self.fr: s = s + " of %s" % str(self.fr)
+		return s
+		
+#	def __getattr__(self, name):
+#		print name
+#		if self._elemdict.has_key(name):
+#			cls = self._elemdict[name]
+#			return DelayedComponentItem(cls, self)
+#	   	if self._propdict.has_key(name):
+#	   		cls = self._propdict[name]
+#	   		return cls(self)
+#		raise AttributeError, name
+		
+		
+class DelayedComponentItem:
+	def __init__(self, compclass, fr):
+		self.compclass = compclass
+		self.fr = fr
+		
+	def __call__(self, which):
+		return self.compclass(which, self.fr)
+		
+	def __repr__(self):
+		return "%s(???, %s)" % (self.__class__.__name__, `self.fr`)
+		
+	def __str__(self):
+		return "selector for element %s of %s"%(self.__class__.__name__, str(self.fr))
+
+template = """
+class %s(ComponentItem): want = '%s'
+"""
+
+exec template % ("Text", 'text')
+exec template % ("Character", 'cha ')
+exec template % ("Word", 'cwor')
+exec template % ("Line", 'clin')
+exec template % ("paragraph", 'cpar')
+exec template % ("Window", 'cwin')
+exec template % ("Document", 'docu')
+exec template % ("File", 'file')
+exec template % ("InsertionPoint", 'cins')
+
diff --git a/Mac/Contrib/PythonScript/getaete.py b/Mac/Contrib/PythonScript/getaete.py
new file mode 100644
index 0000000..14beb32
--- /dev/null
+++ b/Mac/Contrib/PythonScript/getaete.py
@@ -0,0 +1,406 @@
+"""
+Produces a 3 dictionaries from application aete's 
+to be read by PythonScript
+
+v.02 january 31, 1998 added support for inheriting suites from aeut
+v.03 february 16, 1998 changes to identify
+v.04 february 26, 1998 simplified decode
+v.05 23/04/98 simplified _launch
+
+"""
+import baetools
+import macpath
+import sys
+import os
+import MacOS
+import StringIO
+import types
+from MACFS import *
+import macfs
+import string
+from Res import *
+import struct
+
+# for testing only
+app ='CSOm' #'ezVu'# 'nwSP'#MACS'#
+
+#Restrict the application suites to the dialect we want to use.
+LANG = 0 # 0 = English, 1 = French, 11 = Japanese
+lang = {0:'English', 1:'French', 11:'Japanese'}
+
+#The following are neaded to open the application aete
+kASAppleScriptSuite	= 'ascr'
+kGetAETE			= 'gdte'
+attributes = {}
+arguments = {}
+
+class AETE(baetools.TalkTo):
+	pass
+	
+def Getaete(app):
+	try:
+		data = openaete(app)
+	except MacOS.Error, msg:
+		if msg[0] == -609:
+			_launch(app)
+			data = openaete(app)
+	data = decode(data['----'].data)
+	data = compileaete(data)
+	return data
+	
+	
+def decode(data):
+	"""Decode an aete into a python data structure"""
+	f = StringIO.StringIO(data)
+	aete = generic(getaete, f)
+	return aete
+
+def simplify(item):
+	"""Recursively replace singleton tuples by their constituent item"""
+	if type(item) is types.ListType:
+		return map(simplify, item)
+	elif type(item) == types.TupleType and len(item) == 2:
+		return simplify(item[1])
+	else:
+		return item
+
+
+## Here follows the aete resource decoder.
+## It is presented bottom-up instead of top-down because there are  direct
+## references to the lower-level part-decoders from the high-level part-decoders.
+#
+def getflag(f, *args):
+	m = ''
+	c = f.read(2)
+	print `c`
+	if not c:
+		raise EOFError, 'in getflag' + str(args)
+	for n in c:
+		m = m + `ord(n)`
+
+def getbyte(f, *args):
+	c = f.read(1)
+	if not c:
+		raise EOFError, 'in getbyte' + str(args)
+	return ord(c)
+
+def getword(f, *args):
+	getalign(f)
+	s = f.read(2)
+	if len(s) < 2:
+		raise EOFError, 'in getword' + str(args)
+	return (ord(s[0])<<8) | ord(s[1])
+
+def getlong(f, *args):
+	getalign(f)
+	s = f.read(4)
+	if len(s) < 4:
+		raise EOFError, 'in getlong' + str(args)
+	return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
+
+def getostype(f, *args):
+	getalign(f)
+	s = f.read(4)
+	if len(s) < 4:
+		raise EOFError, 'in getostype' + str(args)
+	return s
+
+def getpstr(f, *args):
+	c = f.read(1)
+	if len(c) < 1:
+		raise EOFError, 'in getpstr[1]' + str(args)
+	nbytes = ord(c)
+	if nbytes == 0: return ''
+	s = f.read(nbytes)
+	if len(s) < nbytes:
+		raise EOFError, 'in getpstr[2]' + str(args)
+	return s
+
+def getalign(f):
+	if f.tell() & 1:
+		c = f.read(1)
+		##if c <> '\0':
+		##	print 'align:', `c`
+
+def getlist(f, description, getitem):
+	count = getword(f)
+	list = []
+	for i in range(count):
+		list.append(generic(getitem, f))
+		getalign(f)
+	return list
+
+def alt_generic(what, f, *args):
+	print "generic", `what`, args
+	res = vageneric(what, f, args)
+	print '->', `res`
+	return res
+
+def generic(what, f, *args):
+	if type(what) == types.FunctionType:
+		return apply(what, (f,) + args)
+	if type(what) == types.ListType:
+		record = []
+		for thing in what:
+			item = apply(generic, thing[:1] + (f,) + thing[1:])
+			record.append(item)
+	return record
+	return "BAD GENERIC ARGS: %s" % `what`
+
+getdata = [
+	(getostype, "type"),
+	(getpstr, "description"),
+	(getword, "flags")
+	]
+getargument = [
+	(getpstr, "name"),
+	(getostype, "keyword"),
+	(getdata, "what")
+	]
+getevent = [
+	(getpstr, "name"),
+	(getpstr, "description"),
+	(getostype, "suite code"),
+	(getostype, "event code"),
+	(getdata, "returns"),
+	(getdata, "accepts"),
+	(getlist, "optional arguments", getargument)
+	]
+getproperty = [
+	(getpstr, "name"),
+	(getostype, "code"),
+	(getdata, "what")
+	]
+getelement = [
+	(getostype, "type"),
+	(getlist, "keyform", getostype)
+	]
+getclass = [
+	(getpstr, "name"),
+	(getostype, "class code"),
+	(getpstr, "description"),
+	(getlist, "properties", getproperty),
+	(getlist, "elements", getelement)
+	]
+getcomparison = [
+	(getpstr, "operator name"),
+	(getostype, "operator ID"),
+	(getpstr, "operator comment"),
+	]
+getenumerator = [
+	(getpstr, "enumerator name"),
+	(getostype, "enumerator ID"),
+	(getpstr, "enumerator comment")
+	]
+getenumeration = [
+	(getostype, "enumeration ID"),
+	(getlist, "enumerator", getenumerator)
+	]
+getsuite = [
+	(getpstr, "suite name"),
+	(getpstr, "suite description"),
+	(getostype, "suite ID"),
+	(getword, "suite level"),
+	(getword, "suite version"),
+	(getlist, "events", getevent),
+	(getlist, "classes", getclass),
+	(getlist, "comparisons", getcomparison),
+	(getlist, "enumerations", getenumeration)
+	]
+getaete = [
+	(getbyte, "major version in BCD"),
+	(getbyte, "minor version in BCD"),
+	(getword, "language code"),
+	(getword, "script code"),
+	(getlist, "suites", getsuite)
+	]
+
+def compileaete(aete):
+	"""Generate dictionary for a full aete resource."""
+	[major, minor, language, script, suites] = aete
+	suitedict = {}
+	gsuites = openaeut()
+	for gsuite in gsuites:
+		if gsuite[0] == 'AppleScript Suite':
+			suite = gsuite
+			suite = compilesuite(suite)
+			suitedict[identify(suite[0])] = suite[1:]
+	for suite in suites:
+		if language == LANG:
+			suitecode = suite[2]
+			if suite[5] == []:
+				for gsuite in gsuites:
+					if suitecode == gsuite[2]:
+						suite = gsuite
+			suite = compilesuite(suite)
+			suitedict[identify(suite[0])] = suite[1:]
+	suitedict = combinesuite(suitedict)
+	return suitedict
+			
+def compilesuite(suite):
+	"""Generate dictionary for a single suite"""
+	[name, desc, code, level, version, events, classes, comps, enums] = suite
+	eventdict ={}
+	classdict = {}
+	enumdict ={}
+	for event in events:
+		if event[6]:
+			for ev in event[6]:
+				ev[0] = identify(ev[:2])
+		eventdict[identify(event[:2])] = event[1:]
+	for klass in classes:
+		if klass[3]:
+			for kl in klass[3]:
+				kl[0] = identify(kl[:2])
+		classdict[identify(klass[:2])] = klass[1:]
+	for enum in enums:
+		enumdict[enum[0]] = enum[1]
+	return name, eventdict, classdict, enumdict
+		
+def combinesuite(suite):
+	"""Combines suite dictionaries to seperate event, class, enumeration dictionaries
+	"""
+	
+	suitelist = []
+	eventDict ={}
+	classDict ={}
+	enumDict ={}
+	for value in suite.values():
+		for key in value[0].keys():
+			val = value[0][key]
+			eventDict[key] = val
+		for key in value[1].keys():
+			val = value[1][key]
+			if key in classDict.keys():
+				nval = classDict[key][2]
+				val[2] = val[2] + nval
+			classDict[key] = val
+		for key in value[2].keys():
+			val = value[2][key]
+			enumDict[key] = val
+	return 	eventDict, classDict, enumDict
+		
+
+illegal_ids = [ "for", "in", "from", "and", "or", "not", "print", "class", "return",
+	"def", "name", 'data' ]
+
+def identify(str):
+	"""Turn any string into an identifier:
+	- replace space by _
+	- remove ',' and '-'
+	capitalise
+	"""
+	if not str[0]:
+		if str[1] == 'c@#!':
+			return "Every"
+		else:
+			return 'Any'
+	rv = string.replace(str[0], ' ', '_')
+	rv = string.replace(rv, '-', '')
+	rv = string.replace(rv, ',', '')
+	rv = string.capitalize(rv)
+	return rv
+
+
+def openaete(app):
+	"""open and read the aete of the target application"""
+	arguments['----'] = LANG
+	_aete = AETE(app)
+	_reply, _arguments, _attributes = _aete.send(kASAppleScriptSuite, kGetAETE, arguments, attributes)
+	if _arguments.has_key('errn'):
+		raise baetools.Error, baetools.decodeerror(_arguments)
+	return  _arguments
+
+def openaeut():
+	"""Open and read a aeut file.
+	XXXXX This has been temporarily hard coded until a Python aeut is written XXXX"""
+
+	fullname = dialect
+	rf = OpenRFPerm(fullname, 0, 1)
+	try:
+		UseResFile(rf)
+		resources = []
+		for i in range(Count1Resources('aeut')):
+			res = Get1IndResource('aeut', 1+i)
+			resources.append(res)
+		for res in resources:
+			data = res.data
+			data = decode(data)[4]
+	finally:
+		CloseResFile(rf)
+	return data
+	
+def dialect():
+	"""find the correct Dialect file"""
+
+	dialect = lang[LANG] + " Dialect"
+	try:
+		##System 8
+		vRefNum, dirID = macfs.FindFolder(kOnSystemDisk, kScriptingAdditionsFolderType, 0)
+		fss = macfs.FSSpec((vRefNum, dirID, ''))
+		fss = fss.as_pathname()
+	except macfs.error:
+		##Sytem 7
+		vRefNum, dirID = macfs.FindFolder(kOnSystemDisk, kExtensionFolderType, 0)
+		fss = macfs.FSSpec((vRefNum, dirID, ''))
+		fss = fss.as_pathname()
+		fss = macpath.join(fss, "Scripting Additions")
+	fss = macpath.join(fss, "Dialect")
+	fss = macpath.join(fss, dialect)
+	return fss
+	
+	
+#def openosax():
+#	"""Open and read the aetes of osaxen in the scripting additions folder"""
+#
+#	# System 7.x
+#	aete = []
+#	vRefNum, dirID = macfs.FindFolder(kOnSystemDisk, kExtensionFolderType, 0)
+#	fss = macfs.FSSpec((vRefNum, dirID, ''))
+#	fss = fss.as_pathname()
+#	osax = macpath.join(fss, "Scripting Additions")
+#	for file in os.listdir(osax):
+#		fullname = macpath.join(osax, file)
+#		print fullname
+#		rf = OpenRFPerm(fullname, 0, 1)
+#		try:
+#			UseResFile(rf)
+#			resources = []
+#			for i in range(Count1Resources('aete')):
+#				res = Get1IndResource('aete', 1+i)
+#				resources.append(res)
+#			for res in resources:
+#				data = res.data
+#				data = decode(data)[4]
+#		finally:
+#			CloseResFile(rf)
+#		aete.append(data)
+#	print data
+
+
+#The following should be replaced by direct access to a python 'aeut'
+
+def _launch(appfile):
+	"""Open a file thru the finder. Specify file by name or fsspec"""
+
+#	from PythonScript import PyScript
+	import baetypes
+	_finder = AETE('MACS')
+	parameters ={}
+	parameters['----'] = eval("baetypes.ObjectSpecifier('%s', '%s', %s)" % ('appf', 'ID  ', `appfile`))
+	_reply, _arguments, _attributes = _finder.send( 'aevt', 'odoc', parameters , attributes = {})
+	if _arguments.has_key('errn'):
+		raise baetools.Error, baetools.decodeerror(_arguments)
+	# XXXX Optionally decode result
+	if _arguments.has_key('----'):
+		return _arguments['----']
+
+
+
+if __name__ == '__main__':
+#	import profile
+#	profile.run('Getaete(app)', 'Getaeteprof')
+	Getaete(app)
+#	openosax()
+#	openaete('ascr')
+#	sys.exit(1)
diff --git a/Mac/Contrib/PythonScript/printaete.py b/Mac/Contrib/PythonScript/printaete.py
new file mode 100644
index 0000000..a7fe8e0
--- /dev/null
+++ b/Mac/Contrib/PythonScript/printaete.py
@@ -0,0 +1,346 @@
+"""
+Produces a human readable file of an application's aete
+
+v.02 january 29, 1998 bug fix Main()
+v.03 january 31, 1998 added support for inheriting suites from aeut
+v.04 april 16, 1998 Changed identify to match getaete
+"""
+
+import aetools
+import sys
+import MacOS
+import StringIO
+import types
+import macfs
+import string
+import macpath
+from Res import *
+
+# for testing only
+app = 'MACS'#CSOm'#'nwSP'#'ezVu'#
+
+#Dialect file hard coded as a tempoary measure
+DIALECT = 'Hermit:System Folder:Scripting Additions:Dialects:English Dialect'
+
+#Restrict the application suites to the dialect we want to use.
+LANG = 0 # 0 = English, 1 = French, 11 = Japanese
+
+#The following are neaded to open the application aete
+kASAppleScriptSuite	= 'ascr'
+kGetAETE			= 'gdte'
+attributes = {}
+arguments = {}
+
+class AETE(aetools.TalkTo):
+	pass
+	
+def Main(appl):
+	fss, ok = macfs.PromptGetFile('Application to work on', 'FNDR', 'APPL')#
+	if not ok:
+		return
+	app = fss.GetCreatorType()[0]
+	path = macpath.split(sys.argv[0])[0]
+	appname =  macpath.split(fss.as_pathname())[1]
+	appname = appname + '.aete'
+	appname = macpath.join(path, appname)
+	try:
+		data = Getaete(app)
+	except MacOS.Error, msg:
+		if msg[0] == -609:
+			_launch(app)
+			data = Getaete(app)
+#	print data
+	data = decode(data['----'].data)
+	data = compileaete(data, appname)
+	
+	
+def decode(data):
+	"""Decode an aete into a python data structure"""
+	f = StringIO.StringIO(data)
+	aete = generic(getaete, f)
+	aete = simplify(aete)
+	return aete
+
+def simplify(item):
+	"""Recursively replace singleton tuples by their constituent item"""
+	if type(item) is types.ListType:
+		return map(simplify, item)
+	elif type(item) == types.TupleType and len(item) == 2:
+		return simplify(item[1])
+	else:
+		return item
+
+
+## Here follows the aete resource decoder.
+## It is presented bottom-up instead of top-down because there are  direct
+## references to the lower-level part-decoders from the high-level part-decoders.
+#
+def getbyte(f, *args):
+	c = f.read(1)
+	if not c:
+		raise EOFError, 'in getbyte' + str(args)
+	return ord(c)
+
+def getword(f, *args):
+	getalign(f)
+	s = f.read(2)
+	if len(s) < 2:
+		raise EOFError, 'in getword' + str(args)
+	return (ord(s[0])<<8) | ord(s[1])
+
+def getlong(f, *args):
+	getalign(f)
+	s = f.read(4)
+	if len(s) < 4:
+		raise EOFError, 'in getlong' + str(args)
+	return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
+
+def getostype(f, *args):
+	getalign(f)
+	s = f.read(4)
+	if len(s) < 4:
+		raise EOFError, 'in getostype' + str(args)
+	return s
+
+def getpstr(f, *args):
+	c = f.read(1)
+	if len(c) < 1:
+		raise EOFError, 'in getpstr[1]' + str(args)
+	nbytes = ord(c)
+	if nbytes == 0: return ''
+	s = f.read(nbytes)
+	if len(s) < nbytes:
+		raise EOFError, 'in getpstr[2]' + str(args)
+	return s
+
+def getalign(f):
+	if f.tell() & 1:
+		c = f.read(1)
+		##if c <> '\0':
+		##	print 'align:', `c`
+
+def getlist(f, description, getitem):
+	count = getword(f)
+	list = []
+	for i in range(count):
+		list.append(generic(getitem, f))
+		getalign(f)
+	return list
+
+def alt_generic(what, f, *args):
+	print "generic", `what`, args
+	res = vageneric(what, f, args)
+	print '->', `res`
+	return res
+
+def generic(what, f, *args):
+	if type(what) == types.FunctionType:
+		return apply(what, (f,) + args)
+	if type(what) == types.ListType:
+		record = []
+		for thing in what:
+#			print thing
+			item = apply(generic, thing[:1] + (f,) + thing[1:])
+			record.append((thing[1], item))
+		return record
+	return "BAD GENERIC ARGS: %s" % `what`
+
+getdata = [
+	(getostype, "type"),
+	(getpstr, "description"),
+	(getword, "flags")
+	]
+getargument = [
+	(getpstr, "name"),
+	(getostype, "keyword"),
+	(getdata, "what")
+	]
+getevent = [
+	(getpstr, "name"),
+	(getpstr, "description"),
+	(getostype, "suite code"),
+	(getostype, "event code"),
+	(getdata, "returns"),
+	(getdata, "accepts"),
+	(getlist, "optional arguments", getargument)
+	]
+getproperty = [
+	(getpstr, "name"),
+	(getostype, "code"),
+	(getdata, "what")
+	]
+getelement = [
+	(getostype, "type"),
+	(getlist, "keyform", getostype)
+	]
+getclass = [
+	(getpstr, "name"),
+	(getostype, "class code"),
+	(getpstr, "description"),
+	(getlist, "properties", getproperty),
+	(getlist, "elements", getelement)
+	]
+getcomparison = [
+	(getpstr, "operator name"),
+	(getostype, "operator ID"),
+	(getpstr, "operator comment"),
+	]
+getenumerator = [
+	(getpstr, "enumerator name"),
+	(getostype, "enumerator ID"),
+	(getpstr, "enumerator comment")
+	]
+getenumeration = [
+	(getostype, "enumeration ID"),
+	(getlist, "enumerator", getenumerator)
+	]
+getsuite = [
+	(getpstr, "suite name"),
+	(getpstr, "suite description"),
+	(getostype, "suite ID"),
+	(getword, "suite level"),
+	(getword, "suite version"),
+	(getlist, "events", getevent),
+	(getlist, "classes", getclass),
+	(getlist, "comparisons", getcomparison),
+	(getlist, "enumerations", getenumeration)
+	]
+getaete = [
+	(getword, "major/minor version in BCD"),
+	(getword, "language code"),
+	(getword, "script code"),
+	(getlist, "suites", getsuite)
+	]
+
+def compileaete(aete, appname):
+	"""Generate dictionary file for a full aete resource."""
+	[version, language, script, suites] = aete
+	major, minor = divmod(version, 256)
+	fp = open(appname, 'w')
+	
+	fp.write('%s:\n' % (appname))
+	fp.write("AETE resource version %d/%d, language %d, script %d\n" % \
+		(major, minor, language, script))
+	fp.write('\n\n')
+	gsuites = openaeut()
+	for suite in suites:
+		if language == LANG:
+			suitecode = suite[2]
+			if suite[5] == []:
+				for gsuite in gsuites:
+					if suitecode == gsuite[2]:
+						suite = gsuite
+			[name, desc, code, level, version, events, classes, comps, enums] = suite
+			fp.write('\n%s Suite: %s\n' % (name, desc))
+			fp.write('\n\tEvents:\n')
+			for event in events:
+				fp.write('\n\t%s: %s\n' % (identify(event[0]), event[1]))
+				fp.write('\t\t%s: %s -- %s\n' % (identify(event[0]), event[5][1], event[5][0]))
+				fp.write('\t\tResult: %s -- %s\n' % (event[4][1], event[4][0]))
+				for ev in event[6]:
+					fp.write('\t\t\t%s: %s -- %s\n' % (identify(ev[0]), ev[2][0], ev[2][1]))
+			fp.write('\n\tClasses')
+			for klass in classes:
+				fp.write('\n\t%s: %s\n' % (identify(klass[0]), klass[2]))
+				if klass[3]:
+					if not klass[3][0][0]: continue
+					fp.write('\t\tProperties\n')
+					for cl in klass[3]:
+						fp.write('\t\t\t%s: %s -- %s\n' % (identify(cl[0]), cl[2][1], cl[2][0]))#,, cl[3][3][1]))
+				if klass[4]:
+					fp.write('\n\t\t\tElements\n')
+					for cl in klass[4]:
+						fp.write('\t\t\t\t%s: %s\n' % (identify(cl[0]), cl[1]))
+						
+				
+					
+		
+	
+
+illegal_ids = [ "for", "in", "from", "and", "or", "not", "print", "class", "return",
+	"def", "name" ]
+
+def identify(str):
+	"""Turn any string into an identifier:
+	- replace space by _
+	- prepend _ if the result is a python keyword
+	"""
+
+	rv = string.replace(str, ' ', '_')
+	rv = string.replace(rv, '-', '')
+	rv = string.replace(rv, ',', '')
+	rv = string.capitalize(rv)
+	return rv
+
+
+def Getaete(app):
+	'''Read the target aete'''
+	arguments['----'] = LANG
+	_aete = AETE(app)
+	_reply, _arguments, _attributes = _aete.send('ascr', 'gdte', arguments, attributes)
+	if _arguments.has_key('errn'):
+		raise aetools.Error, aetools.decodeerror(_arguments)
+	return  _arguments
+
+def openaeut():
+	"""Open and read a aeut file.
+	XXXXX This has been temporarily hard coded until a Python aeut is written XXXX"""
+
+	fullname = DIALECT
+	
+	rf = OpenRFPerm(fullname, 0, 1)
+	try:
+		UseResFile(rf)
+		resources = []
+		for i in range(Count1Resources('aeut')):
+			res = Get1IndResource('aeut', 1+i)
+			resources.append(res)
+		for res in resources:
+			data = res.data
+			data = decode(data)[3]
+	finally:
+		CloseResFile(rf)
+	return data
+
+
+#The following should be replaced by direct access to a python 'aeut'
+
+class _miniFinder(aetools.TalkTo):
+	def open(self, _object, _attributes={}, **_arguments):
+		"""open: Open the specified object(s)
+		Required argument: list of objects to open
+		Keyword argument _attributes: AppleEvent attribute dictionary
+		"""
+		_code = 'aevt'
+		_subcode = 'odoc'
+
+		if _arguments: raise TypeError, 'No optional args expected'
+		_arguments['----'] = _object
+
+
+		_reply, _arguments, _attributes = self.send(_code, _subcode,
+				_arguments, _attributes)
+		if _arguments.has_key('errn'):
+			raise aetools.Error, aetools.decodeerror(_arguments)
+		# XXXX Optionally decode result
+		if _arguments.has_key('----'):
+			return _arguments['----']
+	
+_finder = _miniFinder('MACS')
+
+def _launch(appfile):
+	"""Open a file thru the finder. Specify file by name or fsspec"""
+	_finder.open(_application_file(('ID  ', appfile)))
+
+
+class _application_file(aetools.ComponentItem):
+	"""application file - An application's file on disk"""
+	want = 'appf'
+	
+_application_file._propdict = {
+}
+_application_file._elemdict = {
+}
+
+Main(app)
+sys.exit(1)
diff --git a/Mac/Contrib/PythonScript/testeudora.py b/Mac/Contrib/PythonScript/testeudora.py
new file mode 100644
index 0000000..ba676a3
--- /dev/null
+++ b/Mac/Contrib/PythonScript/testeudora.py
@@ -0,0 +1,44 @@
+"""A test program that allows us to control Eudora"""
+
+import sys
+import MacOS
+import PythonScript
+	
+# The Creator signature of eudora:
+SIGNATURE="CSOm"
+TIMEOUT = 10*60*60
+
+
+
+def main():
+	PythonScript.PsScript(SIGNATURE, TIMEOUT)
+	talker = PythonScript.PyScript
+	ev = PythonScript.PsEvents
+	pc = PythonScript.PsClass
+	while 1:
+		print 'get, put, name (of first folder), list (foldernames), quit (eudora) or exit (this program) ?'
+		line = sys.stdin.readline()
+		try:
+			if line[0] == 'g':
+				print 'check'
+				print talker(ev.Activate)
+				print talker(ev.Connect, Checking=1)
+			elif line[0] == 'p':
+				print talker(ev.Connect, Sending=1)
+			elif line[0] == 'n':
+				id = talker(ev.Get, pc.Mail_folder("").Mailbox(1).Name())
+				print "It is called", id, "\n"
+			elif line[0] == 'l':
+				id = talker(ev.Count, pc.Mail_folder(""), Each='Mailbox')
+				print "There are", id, "mailboxes"
+			elif line[0] == 'q':
+				print talker(ev.Quit)
+			elif line[0] == 'e':
+				break
+		except MacOS.Error, arg:
+			if arg[0] == -609:
+				print 'Connection invalid, is eudora running?'
+			else:
+				print 'MacOS Error:', arg[1]
+			
+main()