blob: 205fbdb3fe179c3919102ad1114339bf39388c7b [file] [log] [blame]
Guido van Rossum17448e21995-01-30 11:53:55 +00001# Look for scriptable applications -- that is, applications with an 'aete' resource
2# Also contains (partially) reverse engineered 'aete' resource decoding
3
4import MacOS
5import os
6import string
7import sys
8import types
9import StringIO
10
11from Res import *
12
13def main():
14 filename = raw_input("Listing file? (default stdout): ")
15 redirect(filename, realmain)
16
17def redirect(filename, func, *args):
18 f = filename and open(filename, 'w')
19 save_stdout = sys.stdout
20 try:
21 if f: sys.stdout = f
22 return apply(func, args)
23 finally:
24 sys.stdout = save_stdout
25 if f: f.close()
26
27def realmain():
Guido van Rossum872eeeb1995-02-14 09:47:42 +000028 #list('C:System Folder:Extensions:AppleScript\252')
Guido van Rossum17448e21995-01-30 11:53:55 +000029 #list('C:Tao AppleScript:Finder Liaison:Finder Liaison 1.0')
Guido van Rossum872eeeb1995-02-14 09:47:42 +000030 #list('C:Tao AppleScript:Scriptable Text Editor')
Guido van Rossum17448e21995-01-30 11:53:55 +000031 #list('C:Internet:Eudora 1.4.2:Eudora1.4.2')
Guido van Rossum872eeeb1995-02-14 09:47:42 +000032 #list('E:Excel 4.0:Microsoft Excel')
Guido van Rossum17448e21995-01-30 11:53:55 +000033 #list('C:Internet:Netscape 1.0N:Netscape 1.0N')
34 #find('C:')
Guido van Rossum872eeeb1995-02-14 09:47:42 +000035 find('D:')
36 find('E:')
37 find('F:')
Guido van Rossum17448e21995-01-30 11:53:55 +000038
39def find(dir, maxlevel = 5):
40 hits = []
41 cur = CurResFile()
42 names = os.listdir(dir)
43 tuples = map(lambda x: (os.path.normcase(x), x), names)
44 tuples.sort()
45 names = map(lambda (x, y): y, tuples)
46 for name in names:
47 if name in (os.curdir, os.pardir): continue
48 fullname = os.path.join(dir, name)
49 if os.path.islink(fullname):
50 pass
51 if os.path.isdir(fullname):
52 if maxlevel > 0:
53 sys.stderr.write(" %s\n" % `fullname`)
54 hits = hits + find(fullname, maxlevel-1)
55 else:
56 ctor, type = MacOS.GetCreatorAndType(fullname)
Guido van Rossum872eeeb1995-02-14 09:47:42 +000057 if type in ('APPL', 'FNDR', 'zsys', 'INIT', 'scri', 'cdev'):
Guido van Rossum17448e21995-01-30 11:53:55 +000058 sys.stderr.write(" %s\n" % `fullname`)
59 try:
60 rf = OpenRFPerm(fullname, 0, '\1')
61 except MacOS.Error, msg:
62 print "Error:", fullname, msg
63 continue
64 UseResFile(rf)
65 n = Count1Resources('aete')
66 if rf <> cur:
67 CloseResFile(rf)
68 UseResFile(cur)
69 if n > 1:
70 hits.append(fullname)
71 sys.stderr.write("YES! %d in %s\n" % (n, `fullname`))
72 list(fullname)
73 return hits
74
75def list(fullname):
76 cur = CurResFile()
77 rf = OpenRFPerm(fullname, 0, '\1')
78 try:
79 UseResFile(rf)
80 resources = []
81 for i in range(Count1Resources('aete')):
82 res = Get1IndResource('aete', 1+i)
83 resources.append(res)
84 for i in range(Count1Resources('aeut')):
85 res = Get1IndResource('aeut', 1+i)
86 resources.append(res)
Guido van Rossum872eeeb1995-02-14 09:47:42 +000087 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
Guido van Rossum17448e21995-01-30 11:53:55 +000088 for res in resources:
Guido van Rossum872eeeb1995-02-14 09:47:42 +000089 print "decoding", res.GetResInfo(), "..."
Guido van Rossum17448e21995-01-30 11:53:55 +000090 data = res.data
91 try:
92 aete = decode(data)
93 showaete(aete)
Guido van Rossum872eeeb1995-02-14 09:47:42 +000094 print "Checking putaete..."
Guido van Rossum17448e21995-01-30 11:53:55 +000095 f = StringIO.StringIO()
96 putaete(f, aete)
97 newdata = f.getvalue()
98 if len(newdata) == len(data):
99 if newdata == data:
100 print "putaete created identical data"
101 else:
102 newaete = decode(newdata)
103 if newaete == aete:
104 print "putaete created equivalent data"
105 else:
106 print "putaete failed the test:"
107 showaete(newaete)
108 else:
109 print "putaete created different data:"
110 print `newdata`
111 except:
112 import traceback
113 traceback.print_exc()
114 sys.stdout.flush()
115 finally:
116 if rf <> cur:
117 CloseResFile(rf)
118 UseResFile(cur)
119
120def decode(data):
121 f = StringIO.StringIO(data)
122 aete = generic(getaete, f)
123 aete = simplify(aete)
124 processed = f.tell()
125 unprocessed = len(f.read())
126 total = f.tell()
127 if unprocessed:
128 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
129 (processed, unprocessed, total))
130 return aete
131
132def simplify(item):
133 if type(item) is types.ListType:
134 return map(simplify, item)
135 elif type(item) == types.TupleType and len(item) == 2:
136 return simplify(item[1])
137 else:
138 return item
139
140
141# Here follows the aete resource decoder.
142# It is presented bottom-up instead of top-down because there are direct
143# references to the lower-level part-decoders from the high-level part-decoders.
144
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000145def getbyte(f, *args):
146 c = f.read(1)
147 if not c:
148 raise EOFError, 'in getbyte' + str(args)
149 return ord(c)
150
Guido van Rossum17448e21995-01-30 11:53:55 +0000151def getword(f, *args):
152 getalign(f)
153 s = f.read(2)
154 if len(s) < 2:
155 raise EOFError, 'in getword' + str(args)
156 return (ord(s[0])<<8) | ord(s[1])
157
158def getlong(f, *args):
159 getalign(f)
160 s = f.read(4)
161 if len(s) < 4:
162 raise EOFError, 'in getlong' + str(args)
163 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
164
165def getostype(f, *args):
166 getalign(f)
167 s = f.read(4)
168 if len(s) < 4:
169 raise EOFError, 'in getostype' + str(args)
170 return s
171
172def getpstr(f, *args):
173 c = f.read(1)
174 if len(c) < 1:
175 raise EOFError, 'in getpstr[1]' + str(args)
176 nbytes = ord(c)
177 if nbytes == 0: return ''
178 s = f.read(nbytes)
179 if len(s) < nbytes:
180 raise EOFError, 'in getpstr[2]' + str(args)
181 return s
182
183def getalign(f):
184 if f.tell() & 1:
185 c = f.read(1)
186 ##if c <> '\0':
187 ## print 'align:', `c`
188
189def getlist(f, description, getitem):
190 count = getword(f)
191 list = []
192 for i in range(count):
Guido van Rossum17448e21995-01-30 11:53:55 +0000193 list.append(generic(getitem, f))
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000194 getalign(f)
Guido van Rossum17448e21995-01-30 11:53:55 +0000195 return list
196
197def alt_generic(what, f, *args):
198 print "generic", `what`, args
199 res = vageneric(what, f, args)
200 print '->', `res`
201 return res
202
203def generic(what, f, *args):
204 if type(what) == types.FunctionType:
205 return apply(what, (f,) + args)
206 if type(what) == types.ListType:
207 record = []
208 for thing in what:
209 item = apply(generic, thing[:1] + (f,) + thing[1:])
210 record.append((thing[1], item))
211 return record
212 return "BAD GENERIC ARGS: %s" % `what`
213
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000214getdata = [
215 (getostype, "type"),
216 (getpstr, "description"),
217 (getword, "flags")
218 ]
219getargument = [
220 (getpstr, "name"),
221 (getostype, "keyword"),
222 (getdata, "what")
223 ]
224getevent = [
225 (getpstr, "name"),
226 (getpstr, "description"),
227 (getostype, "suite code"),
228 (getostype, "event code"),
Guido van Rossum17448e21995-01-30 11:53:55 +0000229 (getdata, "returns"),
230 (getdata, "accepts"),
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000231 (getlist, "optional arguments", getargument)
232 ]
233getproperty = [
234 (getpstr, "name"),
235 (getostype, "code"),
236 (getdata, "what")
237 ]
238getelement = [
239 (getostype, "type"),
240 (getlist, "keyform", getostype)
241 ]
242getclass = [
243 (getpstr, "name"),
244 (getostype, "class code"),
245 (getpstr, "description"),
246 (getlist, "properties", getproperty),
247 (getlist, "elements", getelement)
248 ]
249getcomparison = [
250 (getpstr, "operator name"),
251 (getostype, "operator ID"),
252 (getpstr, "operator comment"),
253 ]
254getenumerator = [
255 (getpstr, "enumerator name"),
256 (getostype, "enumerator ID"),
257 (getpstr, "enumerator comment")
258 ]
259getenumeration = [
260 (getostype, "enumeration ID"),
261 (getlist, "enumerator", getenumerator)
262 ]
263getsuite = [
264 (getpstr, "suite name"),
265 (getpstr, "suite description"),
266 (getostype, "suite ID"),
267 (getword, "suite level"),
268 (getword, "suite version"),
269 (getlist, "events", getevent),
Guido van Rossum17448e21995-01-30 11:53:55 +0000270 (getlist, "classes", getclass),
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000271 (getlist, "comparisons", getcomparison),
272 (getlist, "enumerations", getenumeration)
273 ]
274getaete = [
275 (getword, "major/minor version in BCD"),
276 (getword, "language code"),
277 (getword, "script code"),
278 (getlist, "suites", getsuite)
279 ]
Guido van Rossum17448e21995-01-30 11:53:55 +0000280
281
282# Display 'aete' resources in a friendly manner.
283# This one's done top-down again...
284
285def showaete(aete):
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000286 [version, language, script, suites] = aete
287 major, minor = divmod(version, 256)
288 print "\nVersion %d/%d, language %d, script %d" % \
289 (major, minor, language, script)
Guido van Rossum17448e21995-01-30 11:53:55 +0000290 for suite in suites:
291 showsuite(suite)
292
293def showsuite(suite):
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000294 [name, desc, code, level, version, events, classes, comps, enums] = suite
Guido van Rossum17448e21995-01-30 11:53:55 +0000295 print "\nSuite %s -- %s (%s)" % (`name`, `desc`, `code`)
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000296 print "Level %d, version %d" % (level, version)
297 for event in events:
298 showevent(event)
299 for cls in classes:
300 showclass(cls)
301 for comp in comps:
302 showcomparison(comp)
Guido van Rossum17448e21995-01-30 11:53:55 +0000303 for enum in enums:
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000304 showenumeration(enum)
Guido van Rossum17448e21995-01-30 11:53:55 +0000305
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000306def showevent(event):
307 [name, desc, code, subcode, returns, accepts, arguments] = event
Guido van Rossum17448e21995-01-30 11:53:55 +0000308 print "\n Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
309 print " returns", showdata(returns)
310 print " accepts", showdata(accepts)
311 for arg in arguments:
312 showargument(arg)
313
314def showargument(arg):
315 [name, keyword, what] = arg
316 print " %s (%s)" % (name, `keyword`), showdata(what)
317
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000318def showclass(cls):
319 [name, code, desc, properties, elements] = cls
Guido van Rossum17448e21995-01-30 11:53:55 +0000320 print "\n Class %s (%s) -- %s" % (`name`, `code`, `desc`)
321 for prop in properties:
322 showproperty(prop)
323 for elem in elements:
324 showelement(elem)
325
326def showproperty(prop):
327 [name, code, what] = prop
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000328 print " property %s (%s)" % (`name`, `code`), showdata(what)
Guido van Rossum17448e21995-01-30 11:53:55 +0000329
330def showelement(elem):
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000331 [code, keyform] = elem
332 print " element %s" % `code`, "as", keyform
Guido van Rossum17448e21995-01-30 11:53:55 +0000333
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000334def showcomparison(comp):
335 [name, code, comment] = comp
336 print " comparison %s (%s) -- %s" % (`name`, `code`, comment)
337
338def showenumeration(enum):
Guido van Rossum17448e21995-01-30 11:53:55 +0000339 [code, items] = enum
340 print "\n Enum %s" % `code`
341 for item in items:
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000342 showenumerator(item)
Guido van Rossum17448e21995-01-30 11:53:55 +0000343
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000344def showenumerator(item):
Guido van Rossum17448e21995-01-30 11:53:55 +0000345 [name, code, desc] = item
346 print " %s (%s) -- %s" % (`name`, `code`, `desc`)
347
348def showdata(data):
349 [type, description, flags] = data
350 return "%s -- %s %s" % (`type`, `description`, showdataflags(flags))
351
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000352dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
Guido van Rossum17448e21995-01-30 11:53:55 +0000353def showdataflags(flags):
354 bits = []
355 for i in range(16):
356 if flags & (1<<i):
357 if i in dataflagdict.keys():
358 bits.append(dataflagdict[i])
359 else:
360 bits.append(`i`)
361 return '[%s]' % string.join(bits)
362
363
364# Write an 'aete' resource.
365# Closedly modelled after showaete()...
366
367def putaete(f, aete):
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000368 [version, language, script, suites] = aete
369 putword(f, version)
370 putword(f, language)
371 putword(f, script)
Guido van Rossum17448e21995-01-30 11:53:55 +0000372 putlist(f, suites, putsuite)
373
374def putsuite(f, suite):
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000375 [name, desc, code, level, version, events, classes, comps, enums] = suite
Guido van Rossum17448e21995-01-30 11:53:55 +0000376 putpstr(f, name)
377 putpstr(f, desc)
378 putostype(f, code)
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000379 putword(f, level)
380 putword(f, version)
381 putlist(f, events, putevent)
Guido van Rossum17448e21995-01-30 11:53:55 +0000382 putlist(f, classes, putclass)
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000383 putlist(f, comps, putcomparison)
384 putlist(f, enums, putenumeration)
Guido van Rossum17448e21995-01-30 11:53:55 +0000385
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000386def putevent(f, event):
387 [name, desc, eventclass, eventid, returns, accepts, arguments] = event
Guido van Rossum17448e21995-01-30 11:53:55 +0000388 putpstr(f, name)
389 putpstr(f, desc)
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000390 putostype(f, eventclass)
391 putostype(f, eventid)
Guido van Rossum17448e21995-01-30 11:53:55 +0000392 putdata(f, returns)
393 putdata(f, accepts)
394 putlist(f, arguments, putargument)
395
396def putargument(f, arg):
397 [name, keyword, what] = arg
398 putpstr(f, name)
399 putostype(f, keyword)
400 putdata(f, what)
401
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000402def putclass(f, cls):
403 [name, code, desc, properties, elements] = cls
Guido van Rossum17448e21995-01-30 11:53:55 +0000404 putpstr(f, name)
405 putostype(f, code)
406 putpstr(f, desc)
407 putlist(f, properties, putproperty)
408 putlist(f, elements, putelement)
409
410putproperty = putargument
411
412def putelement(f, elem):
413 [code, parts] = elem
414 putostype(f, code)
415 putlist(f, parts, putostype)
416
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000417def putcomparison(f, comp):
418 [name, id, comment] = comp
419 putpstr(f, name)
420 putostype(f, id)
421 putpstr(f, comment)
422
423def putenumeration(f, enum):
Guido van Rossum17448e21995-01-30 11:53:55 +0000424 [code, items] = enum
425 putostype(f, code)
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000426 putlist(f, items, putenumerator)
Guido van Rossum17448e21995-01-30 11:53:55 +0000427
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000428def putenumerator(f, item):
Guido van Rossum17448e21995-01-30 11:53:55 +0000429 [name, code, desc] = item
430 putpstr(f, name)
431 putostype(f, code)
432 putpstr(f, desc)
433
434def putdata(f, data):
435 [type, description, flags] = data
436 putostype(f, type)
437 putpstr(f, description)
438 putword(f, flags)
439
440def putlist(f, list, putitem):
441 putword(f, len(list))
442 for item in list:
Guido van Rossum17448e21995-01-30 11:53:55 +0000443 putitem(f, item)
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000444 putalign(f)
Guido van Rossum17448e21995-01-30 11:53:55 +0000445
446def putalign(f):
447 if f.tell() & 1:
448 f.write('\0')
449
Guido van Rossum872eeeb1995-02-14 09:47:42 +0000450def putbyte(f, value):
451 f.write(chr(value))
452
Guido van Rossum17448e21995-01-30 11:53:55 +0000453def putword(f, value):
454 putalign(f)
455 f.write(chr((value>>8)&0xff))
456 f.write(chr(value&0xff))
457
458def putostype(f, value):
459 putalign(f)
460 if type(value) != types.StringType or len(value) != 4:
461 raise TypeError, "ostype must be 4-char string"
462 f.write(value)
463
464def putpstr(f, value):
465 if type(value) != types.StringType or len(value) > 255:
466 raise TypeError, "pstr must be string <= 255 chars"
467 f.write(chr(len(value)) + value)
468
469
470# Call the main program
471
472if __name__ == '__main__':
473 main()
474else:
475 realmain()