Jack Jansen | 7cc5735 | 1998-08-18 14:54:11 +0000 | [diff] [blame] | 1 | """ |
| 2 | Produces a human readable file of an application's aete |
| 3 | |
| 4 | v.02 january 29, 1998 bug fix Main() |
| 5 | v.03 january 31, 1998 added support for inheriting suites from aeut |
| 6 | v.04 april 16, 1998 Changed identify to match getaete |
| 7 | """ |
| 8 | |
| 9 | import aetools |
| 10 | import sys |
| 11 | import MacOS |
| 12 | import StringIO |
| 13 | import types |
| 14 | import macfs |
| 15 | import string |
| 16 | import macpath |
Jack Jansen | 5a6fdcd | 2001-08-25 12:15:04 +0000 | [diff] [blame^] | 17 | from Carbon.Res import * |
Jack Jansen | 7cc5735 | 1998-08-18 14:54:11 +0000 | [diff] [blame] | 18 | |
| 19 | # for testing only |
| 20 | app = 'MACS'#CSOm'#'nwSP'#'ezVu'# |
| 21 | |
| 22 | #Dialect file hard coded as a tempoary measure |
| 23 | DIALECT = 'Hermit:System Folder:Scripting Additions:Dialects:English Dialect' |
| 24 | |
| 25 | #Restrict the application suites to the dialect we want to use. |
| 26 | LANG = 0 # 0 = English, 1 = French, 11 = Japanese |
| 27 | |
| 28 | #The following are neaded to open the application aete |
| 29 | kASAppleScriptSuite = 'ascr' |
| 30 | kGetAETE = 'gdte' |
| 31 | attributes = {} |
| 32 | arguments = {} |
| 33 | |
| 34 | class AETE(aetools.TalkTo): |
| 35 | pass |
| 36 | |
| 37 | def Main(appl): |
| 38 | fss, ok = macfs.PromptGetFile('Application to work on', 'FNDR', 'APPL')# |
| 39 | if not ok: |
| 40 | return |
| 41 | app = fss.GetCreatorType()[0] |
| 42 | path = macpath.split(sys.argv[0])[0] |
| 43 | appname = macpath.split(fss.as_pathname())[1] |
| 44 | appname = appname + '.aete' |
| 45 | appname = macpath.join(path, appname) |
| 46 | try: |
| 47 | data = Getaete(app) |
| 48 | except MacOS.Error, msg: |
| 49 | if msg[0] == -609: |
| 50 | _launch(app) |
| 51 | data = Getaete(app) |
| 52 | # print data |
| 53 | data = decode(data['----'].data) |
| 54 | data = compileaete(data, appname) |
| 55 | |
| 56 | |
| 57 | def decode(data): |
| 58 | """Decode an aete into a python data structure""" |
| 59 | f = StringIO.StringIO(data) |
| 60 | aete = generic(getaete, f) |
| 61 | aete = simplify(aete) |
| 62 | return aete |
| 63 | |
| 64 | def simplify(item): |
| 65 | """Recursively replace singleton tuples by their constituent item""" |
| 66 | if type(item) is types.ListType: |
| 67 | return map(simplify, item) |
| 68 | elif type(item) == types.TupleType and len(item) == 2: |
| 69 | return simplify(item[1]) |
| 70 | else: |
| 71 | return item |
| 72 | |
| 73 | |
| 74 | ## Here follows the aete resource decoder. |
| 75 | ## It is presented bottom-up instead of top-down because there are direct |
| 76 | ## references to the lower-level part-decoders from the high-level part-decoders. |
| 77 | # |
| 78 | def getbyte(f, *args): |
| 79 | c = f.read(1) |
| 80 | if not c: |
| 81 | raise EOFError, 'in getbyte' + str(args) |
| 82 | return ord(c) |
| 83 | |
| 84 | def getword(f, *args): |
| 85 | getalign(f) |
| 86 | s = f.read(2) |
| 87 | if len(s) < 2: |
| 88 | raise EOFError, 'in getword' + str(args) |
| 89 | return (ord(s[0])<<8) | ord(s[1]) |
| 90 | |
| 91 | def getlong(f, *args): |
| 92 | getalign(f) |
| 93 | s = f.read(4) |
| 94 | if len(s) < 4: |
| 95 | raise EOFError, 'in getlong' + str(args) |
| 96 | return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3]) |
| 97 | |
| 98 | def getostype(f, *args): |
| 99 | getalign(f) |
| 100 | s = f.read(4) |
| 101 | if len(s) < 4: |
| 102 | raise EOFError, 'in getostype' + str(args) |
| 103 | return s |
| 104 | |
| 105 | def getpstr(f, *args): |
| 106 | c = f.read(1) |
| 107 | if len(c) < 1: |
| 108 | raise EOFError, 'in getpstr[1]' + str(args) |
| 109 | nbytes = ord(c) |
| 110 | if nbytes == 0: return '' |
| 111 | s = f.read(nbytes) |
| 112 | if len(s) < nbytes: |
| 113 | raise EOFError, 'in getpstr[2]' + str(args) |
| 114 | return s |
| 115 | |
| 116 | def getalign(f): |
| 117 | if f.tell() & 1: |
| 118 | c = f.read(1) |
| 119 | ##if c <> '\0': |
| 120 | ## print 'align:', `c` |
| 121 | |
| 122 | def getlist(f, description, getitem): |
| 123 | count = getword(f) |
| 124 | list = [] |
| 125 | for i in range(count): |
| 126 | list.append(generic(getitem, f)) |
| 127 | getalign(f) |
| 128 | return list |
| 129 | |
| 130 | def alt_generic(what, f, *args): |
| 131 | print "generic", `what`, args |
| 132 | res = vageneric(what, f, args) |
| 133 | print '->', `res` |
| 134 | return res |
| 135 | |
| 136 | def generic(what, f, *args): |
| 137 | if type(what) == types.FunctionType: |
| 138 | return apply(what, (f,) + args) |
| 139 | if type(what) == types.ListType: |
| 140 | record = [] |
| 141 | for thing in what: |
| 142 | # print thing |
| 143 | item = apply(generic, thing[:1] + (f,) + thing[1:]) |
| 144 | record.append((thing[1], item)) |
| 145 | return record |
| 146 | return "BAD GENERIC ARGS: %s" % `what` |
| 147 | |
| 148 | getdata = [ |
| 149 | (getostype, "type"), |
| 150 | (getpstr, "description"), |
| 151 | (getword, "flags") |
| 152 | ] |
| 153 | getargument = [ |
| 154 | (getpstr, "name"), |
| 155 | (getostype, "keyword"), |
| 156 | (getdata, "what") |
| 157 | ] |
| 158 | getevent = [ |
| 159 | (getpstr, "name"), |
| 160 | (getpstr, "description"), |
| 161 | (getostype, "suite code"), |
| 162 | (getostype, "event code"), |
| 163 | (getdata, "returns"), |
| 164 | (getdata, "accepts"), |
| 165 | (getlist, "optional arguments", getargument) |
| 166 | ] |
| 167 | getproperty = [ |
| 168 | (getpstr, "name"), |
| 169 | (getostype, "code"), |
| 170 | (getdata, "what") |
| 171 | ] |
| 172 | getelement = [ |
| 173 | (getostype, "type"), |
| 174 | (getlist, "keyform", getostype) |
| 175 | ] |
| 176 | getclass = [ |
| 177 | (getpstr, "name"), |
| 178 | (getostype, "class code"), |
| 179 | (getpstr, "description"), |
| 180 | (getlist, "properties", getproperty), |
| 181 | (getlist, "elements", getelement) |
| 182 | ] |
| 183 | getcomparison = [ |
| 184 | (getpstr, "operator name"), |
| 185 | (getostype, "operator ID"), |
| 186 | (getpstr, "operator comment"), |
| 187 | ] |
| 188 | getenumerator = [ |
| 189 | (getpstr, "enumerator name"), |
| 190 | (getostype, "enumerator ID"), |
| 191 | (getpstr, "enumerator comment") |
| 192 | ] |
| 193 | getenumeration = [ |
| 194 | (getostype, "enumeration ID"), |
| 195 | (getlist, "enumerator", getenumerator) |
| 196 | ] |
| 197 | getsuite = [ |
| 198 | (getpstr, "suite name"), |
| 199 | (getpstr, "suite description"), |
| 200 | (getostype, "suite ID"), |
| 201 | (getword, "suite level"), |
| 202 | (getword, "suite version"), |
| 203 | (getlist, "events", getevent), |
| 204 | (getlist, "classes", getclass), |
| 205 | (getlist, "comparisons", getcomparison), |
| 206 | (getlist, "enumerations", getenumeration) |
| 207 | ] |
| 208 | getaete = [ |
| 209 | (getword, "major/minor version in BCD"), |
| 210 | (getword, "language code"), |
| 211 | (getword, "script code"), |
| 212 | (getlist, "suites", getsuite) |
| 213 | ] |
| 214 | |
| 215 | def compileaete(aete, appname): |
| 216 | """Generate dictionary file for a full aete resource.""" |
| 217 | [version, language, script, suites] = aete |
| 218 | major, minor = divmod(version, 256) |
| 219 | fp = open(appname, 'w') |
| 220 | |
| 221 | fp.write('%s:\n' % (appname)) |
| 222 | fp.write("AETE resource version %d/%d, language %d, script %d\n" % \ |
| 223 | (major, minor, language, script)) |
| 224 | fp.write('\n\n') |
| 225 | gsuites = openaeut() |
| 226 | for suite in suites: |
| 227 | if language == LANG: |
| 228 | suitecode = suite[2] |
| 229 | if suite[5] == []: |
| 230 | for gsuite in gsuites: |
| 231 | if suitecode == gsuite[2]: |
| 232 | suite = gsuite |
| 233 | [name, desc, code, level, version, events, classes, comps, enums] = suite |
| 234 | fp.write('\n%s Suite: %s\n' % (name, desc)) |
| 235 | fp.write('\n\tEvents:\n') |
| 236 | for event in events: |
| 237 | fp.write('\n\t%s: %s\n' % (identify(event[0]), event[1])) |
| 238 | fp.write('\t\t%s: %s -- %s\n' % (identify(event[0]), event[5][1], event[5][0])) |
| 239 | fp.write('\t\tResult: %s -- %s\n' % (event[4][1], event[4][0])) |
| 240 | for ev in event[6]: |
| 241 | fp.write('\t\t\t%s: %s -- %s\n' % (identify(ev[0]), ev[2][0], ev[2][1])) |
| 242 | fp.write('\n\tClasses') |
| 243 | for klass in classes: |
| 244 | fp.write('\n\t%s: %s\n' % (identify(klass[0]), klass[2])) |
| 245 | if klass[3]: |
| 246 | if not klass[3][0][0]: continue |
| 247 | fp.write('\t\tProperties\n') |
| 248 | for cl in klass[3]: |
| 249 | fp.write('\t\t\t%s: %s -- %s\n' % (identify(cl[0]), cl[2][1], cl[2][0]))#,, cl[3][3][1])) |
| 250 | if klass[4]: |
| 251 | fp.write('\n\t\t\tElements\n') |
| 252 | for cl in klass[4]: |
| 253 | fp.write('\t\t\t\t%s: %s\n' % (identify(cl[0]), cl[1])) |
| 254 | |
| 255 | |
| 256 | |
| 257 | |
| 258 | |
| 259 | |
| 260 | illegal_ids = [ "for", "in", "from", "and", "or", "not", "print", "class", "return", |
| 261 | "def", "name" ] |
| 262 | |
| 263 | def identify(str): |
| 264 | """Turn any string into an identifier: |
| 265 | - replace space by _ |
| 266 | - prepend _ if the result is a python keyword |
| 267 | """ |
| 268 | |
| 269 | rv = string.replace(str, ' ', '_') |
| 270 | rv = string.replace(rv, '-', '') |
| 271 | rv = string.replace(rv, ',', '') |
| 272 | rv = string.capitalize(rv) |
| 273 | return rv |
| 274 | |
| 275 | |
| 276 | def Getaete(app): |
| 277 | '''Read the target aete''' |
| 278 | arguments['----'] = LANG |
| 279 | _aete = AETE(app) |
| 280 | _reply, _arguments, _attributes = _aete.send('ascr', 'gdte', arguments, attributes) |
| 281 | if _arguments.has_key('errn'): |
| 282 | raise aetools.Error, aetools.decodeerror(_arguments) |
| 283 | return _arguments |
| 284 | |
| 285 | def openaeut(): |
| 286 | """Open and read a aeut file. |
| 287 | XXXXX This has been temporarily hard coded until a Python aeut is written XXXX""" |
| 288 | |
| 289 | fullname = DIALECT |
| 290 | |
| 291 | rf = OpenRFPerm(fullname, 0, 1) |
| 292 | try: |
| 293 | UseResFile(rf) |
| 294 | resources = [] |
| 295 | for i in range(Count1Resources('aeut')): |
| 296 | res = Get1IndResource('aeut', 1+i) |
| 297 | resources.append(res) |
| 298 | for res in resources: |
| 299 | data = res.data |
| 300 | data = decode(data)[3] |
| 301 | finally: |
| 302 | CloseResFile(rf) |
| 303 | return data |
| 304 | |
| 305 | |
| 306 | #The following should be replaced by direct access to a python 'aeut' |
| 307 | |
| 308 | class _miniFinder(aetools.TalkTo): |
| 309 | def open(self, _object, _attributes={}, **_arguments): |
| 310 | """open: Open the specified object(s) |
| 311 | Required argument: list of objects to open |
| 312 | Keyword argument _attributes: AppleEvent attribute dictionary |
| 313 | """ |
| 314 | _code = 'aevt' |
| 315 | _subcode = 'odoc' |
| 316 | |
| 317 | if _arguments: raise TypeError, 'No optional args expected' |
| 318 | _arguments['----'] = _object |
| 319 | |
| 320 | |
| 321 | _reply, _arguments, _attributes = self.send(_code, _subcode, |
| 322 | _arguments, _attributes) |
| 323 | if _arguments.has_key('errn'): |
| 324 | raise aetools.Error, aetools.decodeerror(_arguments) |
| 325 | # XXXX Optionally decode result |
| 326 | if _arguments.has_key('----'): |
| 327 | return _arguments['----'] |
| 328 | |
| 329 | _finder = _miniFinder('MACS') |
| 330 | |
| 331 | def _launch(appfile): |
| 332 | """Open a file thru the finder. Specify file by name or fsspec""" |
| 333 | _finder.open(_application_file(('ID ', appfile))) |
| 334 | |
| 335 | |
| 336 | class _application_file(aetools.ComponentItem): |
| 337 | """application file - An application's file on disk""" |
| 338 | want = 'appf' |
| 339 | |
| 340 | _application_file._propdict = { |
| 341 | } |
| 342 | _application_file._elemdict = { |
| 343 | } |
| 344 | |
| 345 | Main(app) |
| 346 | sys.exit(1) |