blob: 5a91d8921bb96d46b26c555cfa695ea5ea482800 [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():
28 #list('C:Tao AppleScript:Finder Liaison:Finder Liaison 1.0')
29 #list('C:Internet:Eudora 1.4.2:Eudora1.4.2')
30 list('E:Excel 4.0:Microsoft Excel')
31 #list('C:Internet:Netscape 1.0N:Netscape 1.0N')
32 #find('C:')
33 #find('D:')
34 #find('E:')
35 #find('F:')
36
37def find(dir, maxlevel = 5):
38 hits = []
39 cur = CurResFile()
40 names = os.listdir(dir)
41 tuples = map(lambda x: (os.path.normcase(x), x), names)
42 tuples.sort()
43 names = map(lambda (x, y): y, tuples)
44 for name in names:
45 if name in (os.curdir, os.pardir): continue
46 fullname = os.path.join(dir, name)
47 if os.path.islink(fullname):
48 pass
49 if os.path.isdir(fullname):
50 if maxlevel > 0:
51 sys.stderr.write(" %s\n" % `fullname`)
52 hits = hits + find(fullname, maxlevel-1)
53 else:
54 ctor, type = MacOS.GetCreatorAndType(fullname)
55 if type == 'APPL':
56 sys.stderr.write(" %s\n" % `fullname`)
57 try:
58 rf = OpenRFPerm(fullname, 0, '\1')
59 except MacOS.Error, msg:
60 print "Error:", fullname, msg
61 continue
62 UseResFile(rf)
63 n = Count1Resources('aete')
64 if rf <> cur:
65 CloseResFile(rf)
66 UseResFile(cur)
67 if n > 1:
68 hits.append(fullname)
69 sys.stderr.write("YES! %d in %s\n" % (n, `fullname`))
70 list(fullname)
71 return hits
72
73def list(fullname):
74 cur = CurResFile()
75 rf = OpenRFPerm(fullname, 0, '\1')
76 try:
77 UseResFile(rf)
78 resources = []
79 for i in range(Count1Resources('aete')):
80 res = Get1IndResource('aete', 1+i)
81 resources.append(res)
82 for i in range(Count1Resources('aeut')):
83 res = Get1IndResource('aeut', 1+i)
84 resources.append(res)
85 print "\nLISTING aete+aeut RESOURCE IN", `fullname`
86 for res in resources:
87 print res.GetResInfo()
88 data = res.data
89 try:
90 aete = decode(data)
91 showaete(aete)
92 f = StringIO.StringIO()
93 putaete(f, aete)
94 newdata = f.getvalue()
95 if len(newdata) == len(data):
96 if newdata == data:
97 print "putaete created identical data"
98 else:
99 newaete = decode(newdata)
100 if newaete == aete:
101 print "putaete created equivalent data"
102 else:
103 print "putaete failed the test:"
104 showaete(newaete)
105 else:
106 print "putaete created different data:"
107 print `newdata`
108 except:
109 import traceback
110 traceback.print_exc()
111 sys.stdout.flush()
112 finally:
113 if rf <> cur:
114 CloseResFile(rf)
115 UseResFile(cur)
116
117def decode(data):
118 f = StringIO.StringIO(data)
119 aete = generic(getaete, f)
120 aete = simplify(aete)
121 processed = f.tell()
122 unprocessed = len(f.read())
123 total = f.tell()
124 if unprocessed:
125 sys.stderr.write("%d processed + %d unprocessed = %d total\n" %
126 (processed, unprocessed, total))
127 return aete
128
129def simplify(item):
130 if type(item) is types.ListType:
131 return map(simplify, item)
132 elif type(item) == types.TupleType and len(item) == 2:
133 return simplify(item[1])
134 else:
135 return item
136
137
138# Here follows the aete resource decoder.
139# It is presented bottom-up instead of top-down because there are direct
140# references to the lower-level part-decoders from the high-level part-decoders.
141
142def getword(f, *args):
143 getalign(f)
144 s = f.read(2)
145 if len(s) < 2:
146 raise EOFError, 'in getword' + str(args)
147 return (ord(s[0])<<8) | ord(s[1])
148
149def getlong(f, *args):
150 getalign(f)
151 s = f.read(4)
152 if len(s) < 4:
153 raise EOFError, 'in getlong' + str(args)
154 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
155
156def getostype(f, *args):
157 getalign(f)
158 s = f.read(4)
159 if len(s) < 4:
160 raise EOFError, 'in getostype' + str(args)
161 return s
162
163def getpstr(f, *args):
164 c = f.read(1)
165 if len(c) < 1:
166 raise EOFError, 'in getpstr[1]' + str(args)
167 nbytes = ord(c)
168 if nbytes == 0: return ''
169 s = f.read(nbytes)
170 if len(s) < nbytes:
171 raise EOFError, 'in getpstr[2]' + str(args)
172 return s
173
174def getalign(f):
175 if f.tell() & 1:
176 c = f.read(1)
177 ##if c <> '\0':
178 ## print 'align:', `c`
179
180def getlist(f, description, getitem):
181 count = getword(f)
182 list = []
183 for i in range(count):
184 getalign(f)
185 list.append(generic(getitem, f))
186 return list
187
188def alt_generic(what, f, *args):
189 print "generic", `what`, args
190 res = vageneric(what, f, args)
191 print '->', `res`
192 return res
193
194def generic(what, f, *args):
195 if type(what) == types.FunctionType:
196 return apply(what, (f,) + args)
197 if type(what) == types.ListType:
198 record = []
199 for thing in what:
200 item = apply(generic, thing[:1] + (f,) + thing[1:])
201 record.append((thing[1], item))
202 return record
203 return "BAD GENERIC ARGS: %s" % `what`
204
205getdata = [(getostype, "type"), (getpstr, "description"), (getword, "flags")]
206getoptarg = [(getpstr, "name"), (getostype, "keyword"), (getdata, "what")]
207getcommand = [(getpstr, "name"), (getpstr, "description"),
208 (getostype, "suite code"), (getostype, "command code"),
209 (getdata, "returns"),
210 (getdata, "accepts"),
211 (getlist, "optional arguments", getoptarg)]
212getprop = [(getpstr, "name"), (getostype, "code"), (getdata, "what")]
213getelem = [(getostype, "type"), (getlist, "accessibility", getostype)]
214getclass = [(getpstr, "name"), (getostype, "class code"), (getpstr, "description"),
215 (getlist, "properties", getprop), (getlist, "elements", getelem)]
216getenumitem = [(getpstr, "name"), (getostype, "value"), (getpstr, "description")]
217getenum = [(getostype, "enumtype"), (getlist, "enumitem", getenumitem)]
218getsuite = [(getpstr, "name"), (getpstr, "description"), (getostype, "code"),
219 (getword, "flags1"), (getword, "flags2"),
220 (getlist, "commands", getcommand),
221 (getlist, "classes", getclass),
222 (getword, "count???"), (getlist, "enums", getenum)]
223getaete = [(getword, "skip1"), (getword, "skip2"), (getword, "skip3"),
224 (getlist, "suites", getsuite)]
225
226
227# Display 'aete' resources in a friendly manner.
228# This one's done top-down again...
229
230def showaete(aete):
231 [flags1, flags2, flags3, suites] = aete
232 print "\nGlobal flags: x%x, x%x, x%x\n" % (flags1, flags2, flags3)
233 for suite in suites:
234 showsuite(suite)
235
236def showsuite(suite):
237 [name, desc, code, flags1, flags2, commands, classes, skip1, enums] = suite
238 print "\nSuite %s -- %s (%s)" % (`name`, `desc`, `code`)
239 for command in commands:
240 showcommand(command)
241 for classe in classes:
242 showclass(classe)
243 for enum in enums:
244 showenum(enum)
245
246def showcommand(command):
247 [name, desc, code, subcode, returns, accepts, arguments] = command
248 print "\n Command %s -- %s (%s, %s)" % (`name`, `desc`, `code`, `subcode`)
249 print " returns", showdata(returns)
250 print " accepts", showdata(accepts)
251 for arg in arguments:
252 showargument(arg)
253
254def showargument(arg):
255 [name, keyword, what] = arg
256 print " %s (%s)" % (name, `keyword`), showdata(what)
257
258def showclass(classe):
259 [name, code, desc, properties, elements] = classe
260 print "\n Class %s (%s) -- %s" % (`name`, `code`, `desc`)
261 for prop in properties:
262 showproperty(prop)
263 for elem in elements:
264 showelement(elem)
265
266def showproperty(prop):
267 [name, code, what] = prop
268 print " property %s (%s)" % (name, code), showdata(what)
269
270def showelement(elem):
271 [code, accessibility] = elem
272 print " element %s" % `code`, "as", accessibility
273
274def showenum(enum):
275 [code, items] = enum
276 print "\n Enum %s" % `code`
277 for item in items:
278 showitem(item)
279
280def showitem(item):
281 [name, code, desc] = item
282 print " %s (%s) -- %s" % (`name`, `code`, `desc`)
283
284def showdata(data):
285 [type, description, flags] = data
286 return "%s -- %s %s" % (`type`, `description`, showdataflags(flags))
287
288dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "writable"}
289def showdataflags(flags):
290 bits = []
291 for i in range(16):
292 if flags & (1<<i):
293 if i in dataflagdict.keys():
294 bits.append(dataflagdict[i])
295 else:
296 bits.append(`i`)
297 return '[%s]' % string.join(bits)
298
299
300# Write an 'aete' resource.
301# Closedly modelled after showaete()...
302
303def putaete(f, aete):
304 [flags1, flags2, flags3, suites] = aete
305 putword(f, flags1)
306 putword(f, flags2)
307 putword(f, flags3)
308 putlist(f, suites, putsuite)
309
310def putsuite(f, suite):
311 [name, desc, code, flags1, flags2, commands, classes, skip1, enums] = suite
312 putpstr(f, name)
313 putpstr(f, desc)
314 putostype(f, code)
315 putword(f, flags1)
316 putword(f, flags2)
317 putlist(f, commands, putcommand)
318 putlist(f, classes, putclass)
319 putword(f, skip1)
320 putlist(f, enums, putenum)
321
322def putcommand(f, command):
323 [name, desc, code, subcode, returns, accepts, arguments] = command
324 putpstr(f, name)
325 putpstr(f, desc)
326 putostype(f, code)
327 putostype(f, subcode)
328 putdata(f, returns)
329 putdata(f, accepts)
330 putlist(f, arguments, putargument)
331
332def putargument(f, arg):
333 [name, keyword, what] = arg
334 putpstr(f, name)
335 putostype(f, keyword)
336 putdata(f, what)
337
338def putclass(f, classe):
339 [name, code, desc, properties, elements] = classe
340 putpstr(f, name)
341 putostype(f, code)
342 putpstr(f, desc)
343 putlist(f, properties, putproperty)
344 putlist(f, elements, putelement)
345
346putproperty = putargument
347
348def putelement(f, elem):
349 [code, parts] = elem
350 putostype(f, code)
351 putlist(f, parts, putostype)
352
353def putenum(f, enum):
354 [code, items] = enum
355 putostype(f, code)
356 putlist(f, items, putitem)
357
358def putitem(f, item):
359 [name, code, desc] = item
360 putpstr(f, name)
361 putostype(f, code)
362 putpstr(f, desc)
363
364def putdata(f, data):
365 [type, description, flags] = data
366 putostype(f, type)
367 putpstr(f, description)
368 putword(f, flags)
369
370def putlist(f, list, putitem):
371 putword(f, len(list))
372 for item in list:
373 putalign(f)
374 putitem(f, item)
375
376def putalign(f):
377 if f.tell() & 1:
378 f.write('\0')
379
380def putword(f, value):
381 putalign(f)
382 f.write(chr((value>>8)&0xff))
383 f.write(chr(value&0xff))
384
385def putostype(f, value):
386 putalign(f)
387 if type(value) != types.StringType or len(value) != 4:
388 raise TypeError, "ostype must be 4-char string"
389 f.write(value)
390
391def putpstr(f, value):
392 if type(value) != types.StringType or len(value) > 255:
393 raise TypeError, "pstr must be string <= 255 chars"
394 f.write(chr(len(value)) + value)
395
396
397# Call the main program
398
399if __name__ == '__main__':
400 main()
401else:
402 realmain()