Oops -- Lib/Test should be Lib/test, of course!
[python/dscho.git] / Mac / Lib / test / aete.py
blob21fff6189b425f4507933591d33199dd8bc6dba9
1 # Look for scriptable applications -- that is, applications with an 'aete' resource
2 # Also contains (partially) reverse engineered 'aete' resource decoding
4 import MacOS
5 import os
6 import string
7 import sys
8 import types
9 import StringIO
11 from Res import *
13 def main():
14 filename = ""
15 redirect(filename, realmain)
17 def 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()
27 def realmain():
28 #list('C:System Folder:Extensions:AppleScript\252')
29 #list('C:Tao AppleScript:Finder Liaison:Finder Liaison 1.0')
30 list('C:Tao AppleScript:Scriptable Text Editor')
31 #list('C:Internet:Eudora 1.4.2:Eudora1.4.2')
32 #list('E:Excel 4.0:Microsoft Excel')
33 #list('C:Internet:Netscape 1.0N:Netscape 1.0N')
34 #find('C:')
35 #find('D:')
36 #find('E:')
37 #find('F:')
39 def 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)
57 if type in ('APPL', 'FNDR', 'zsys', 'INIT', 'scri', 'cdev'):
58 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
75 def 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)
87 print "\nLISTING aete+aeut RESOURCES IN", `fullname`
88 for res in resources:
89 print "decoding", res.GetResInfo(), "..."
90 data = res.data
91 try:
92 aete = decode(data)
93 showaete(aete)
94 print "Checking putaete..."
95 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)
120 def 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
132 def 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
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.
145 def getbyte(f, *args):
146 c = f.read(1)
147 if not c:
148 raise EOFError, 'in getbyte' + str(args)
149 return ord(c)
151 def 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])
158 def 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])
165 def 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
172 def 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
183 def getalign(f):
184 if f.tell() & 1:
185 c = f.read(1)
186 ##if c <> '\0':
187 ## print 'align:', `c`
189 def getlist(f, description, getitem):
190 count = getword(f)
191 list = []
192 for i in range(count):
193 list.append(generic(getitem, f))
194 getalign(f)
195 return list
197 def alt_generic(what, f, *args):
198 print "generic", `what`, args
199 res = vageneric(what, f, args)
200 print '->', `res`
201 return res
203 def 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`
214 getdata = [
215 (getostype, "type"),
216 (getpstr, "description"),
217 (getword, "flags")
219 getargument = [
220 (getpstr, "name"),
221 (getostype, "keyword"),
222 (getdata, "what")
224 getevent = [
225 (getpstr, "name"),
226 (getpstr, "description"),
227 (getostype, "suite code"),
228 (getostype, "event code"),
229 (getdata, "returns"),
230 (getdata, "accepts"),
231 (getlist, "optional arguments", getargument)
233 getproperty = [
234 (getpstr, "name"),
235 (getostype, "code"),
236 (getdata, "what")
238 getelement = [
239 (getostype, "type"),
240 (getlist, "keyform", getostype)
242 getclass = [
243 (getpstr, "name"),
244 (getostype, "class code"),
245 (getpstr, "description"),
246 (getlist, "properties", getproperty),
247 (getlist, "elements", getelement)
249 getcomparison = [
250 (getpstr, "operator name"),
251 (getostype, "operator ID"),
252 (getpstr, "operator comment"),
254 getenumerator = [
255 (getpstr, "enumerator name"),
256 (getostype, "enumerator ID"),
257 (getpstr, "enumerator comment")
259 getenumeration = [
260 (getostype, "enumeration ID"),
261 (getlist, "enumerator", getenumerator)
263 getsuite = [
264 (getpstr, "suite name"),
265 (getpstr, "suite description"),
266 (getostype, "suite ID"),
267 (getword, "suite level"),
268 (getword, "suite version"),
269 (getlist, "events", getevent),
270 (getlist, "classes", getclass),
271 (getlist, "comparisons", getcomparison),
272 (getlist, "enumerations", getenumeration)
274 getaete = [
275 (getword, "major/minor version in BCD"),
276 (getword, "language code"),
277 (getword, "script code"),
278 (getlist, "suites", getsuite)
282 # Display 'aete' resources in a friendly manner.
283 # This one's done top-down again...
285 def showaete(aete):
286 [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)
290 for suite in suites:
291 showsuite(suite)
293 def showsuite(suite):
294 [name, desc, code, level, version, events, classes, comps, enums] = suite
295 print "\nSuite %s -- %s (%s)" % (`name`, `desc`, `code`)
296 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)
303 for enum in enums:
304 showenumeration(enum)
306 def showevent(event):
307 [name, desc, code, subcode, returns, accepts, arguments] = event
308 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)
314 def showargument(arg):
315 [name, keyword, what] = arg
316 print " %s (%s)" % (name, `keyword`), showdata(what)
318 def showclass(cls):
319 [name, code, desc, properties, elements] = cls
320 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)
326 def showproperty(prop):
327 [name, code, what] = prop
328 print " property %s (%s)" % (`name`, `code`), showdata(what)
330 def showelement(elem):
331 [code, keyform] = elem
332 print " element %s" % `code`, "as", keyform
334 def showcomparison(comp):
335 [name, code, comment] = comp
336 print " comparison %s (%s) -- %s" % (`name`, `code`, comment)
338 def showenumeration(enum):
339 [code, items] = enum
340 print "\n Enum %s" % `code`
341 for item in items:
342 showenumerator(item)
344 def showenumerator(item):
345 [name, code, desc] = item
346 print " %s (%s) -- %s" % (`name`, `code`, `desc`)
348 def showdata(data):
349 [type, description, flags] = data
350 return "%s -- %s %s" % (`type`, `description`, showdataflags(flags))
352 dataflagdict = {15: "optional", 14: "list", 13: "enum", 12: "mutable"}
353 def 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)
364 # Write an 'aete' resource.
365 # Closedly modelled after showaete()...
367 def putaete(f, aete):
368 [version, language, script, suites] = aete
369 putword(f, version)
370 putword(f, language)
371 putword(f, script)
372 putlist(f, suites, putsuite)
374 def putsuite(f, suite):
375 [name, desc, code, level, version, events, classes, comps, enums] = suite
376 putpstr(f, name)
377 putpstr(f, desc)
378 putostype(f, code)
379 putword(f, level)
380 putword(f, version)
381 putlist(f, events, putevent)
382 putlist(f, classes, putclass)
383 putlist(f, comps, putcomparison)
384 putlist(f, enums, putenumeration)
386 def putevent(f, event):
387 [name, desc, eventclass, eventid, returns, accepts, arguments] = event
388 putpstr(f, name)
389 putpstr(f, desc)
390 putostype(f, eventclass)
391 putostype(f, eventid)
392 putdata(f, returns)
393 putdata(f, accepts)
394 putlist(f, arguments, putargument)
396 def putargument(f, arg):
397 [name, keyword, what] = arg
398 putpstr(f, name)
399 putostype(f, keyword)
400 putdata(f, what)
402 def putclass(f, cls):
403 [name, code, desc, properties, elements] = cls
404 putpstr(f, name)
405 putostype(f, code)
406 putpstr(f, desc)
407 putlist(f, properties, putproperty)
408 putlist(f, elements, putelement)
410 putproperty = putargument
412 def putelement(f, elem):
413 [code, parts] = elem
414 putostype(f, code)
415 putlist(f, parts, putostype)
417 def putcomparison(f, comp):
418 [name, id, comment] = comp
419 putpstr(f, name)
420 putostype(f, id)
421 putpstr(f, comment)
423 def putenumeration(f, enum):
424 [code, items] = enum
425 putostype(f, code)
426 putlist(f, items, putenumerator)
428 def putenumerator(f, item):
429 [name, code, desc] = item
430 putpstr(f, name)
431 putostype(f, code)
432 putpstr(f, desc)
434 def putdata(f, data):
435 [type, description, flags] = data
436 putostype(f, type)
437 putpstr(f, description)
438 putword(f, flags)
440 def putlist(f, list, putitem):
441 putword(f, len(list))
442 for item in list:
443 putitem(f, item)
444 putalign(f)
446 def putalign(f):
447 if f.tell() & 1:
448 f.write('\0')
450 def putbyte(f, value):
451 f.write(chr(value))
453 def putword(f, value):
454 putalign(f)
455 f.write(chr((value>>8)&0xff))
456 f.write(chr(value&0xff))
458 def 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)
464 def 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)
470 # Call the main program
472 if __name__ == '__main__':
473 main()
474 else:
475 realmain()