From 3997f58a0c147cee83203ef731e2541c10557335 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Tue, 18 Nov 2003 23:09:19 +0000 Subject: Modified version by Bob Ippolito. It passes the just-added test_applesingle after some minor mods. Fixes #803498, but should NOT be backported because the original problem seems to be unreproducable. --- Lib/plat-mac/applesingle.py | 163 +++++++++++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 63 deletions(-) diff --git a/Lib/plat-mac/applesingle.py b/Lib/plat-mac/applesingle.py index 5b9c2dd..adbce0c 100644 --- a/Lib/plat-mac/applesingle.py +++ b/Lib/plat-mac/applesingle.py @@ -1,14 +1,31 @@ -# applesingle - a module to decode AppleSingle files +r"""Routines to decode AppleSingle files +""" import struct -import MacOS import sys +try: + import MacOS + import Carbon.File +except: + class MacOS: + def openrf(path, mode): + return open(path + '.rsrc', mode) + openrf = classmethod(openrf) + class Carbon: + class File: + class FSSpec: + pass + class FSRef: + pass + class Alias: + pass -Error="applesingle.Error" - -verbose=0 +# all of the errors in this module are really errors in the input +# so I think it should test positive against ValueError. +class Error(ValueError): + pass # File header format: magic, version, unused, number of entries -AS_HEADER_FORMAT="ll16sh" +AS_HEADER_FORMAT="LL16sh" AS_HEADER_LENGTH=26 # The flag words for AppleSingle AS_MAGIC=0x00051600 @@ -23,78 +40,98 @@ AS_DATAFORK=1 AS_RESOURCEFORK=2 AS_IGNORE=(3,4,5,6,8,9,10,11,12,13,14,15) -def decode(input, output, resonly=0): - if type(input) == type(''): - input = open(input, 'rb') - # Should we also test for FSSpecs or FSRefs? - header = input.read(AS_HEADER_LENGTH) - try: - magic, version, dummy, nentry = struct.unpack(AS_HEADER_FORMAT, header) - except ValueError, arg: - raise Error, "Unpack header error: %s"%arg - if verbose: - print 'Magic: 0x%8.8x'%magic - print 'Version: 0x%8.8x'%version - print 'Entries: %d'%nentry - if magic != AS_MAGIC: - raise Error, 'Unknown AppleSingle magic number 0x%8.8x'%magic - if version != AS_VERSION: - raise Error, 'Unknown AppleSingle version number 0x%8.8x'%version - if nentry <= 0: - raise Error, "AppleSingle file contains no forks" - headers = [input.read(AS_ENTRY_LENGTH) for i in range(nentry)] - didwork = 0 - for hdr in headers: +class AppleSingle(object): + datafork = None + resourcefork = None + + def __init__(self, fileobj, verbose=False): + header = fileobj.read(AS_HEADER_LENGTH) try: - id, offset, length = struct.unpack(AS_ENTRY_FORMAT, hdr) + magic, version, ig, nentry = struct.unpack(AS_HEADER_FORMAT, header) except ValueError, arg: - raise Error, "Unpack entry error: %s"%arg + raise Error, "Unpack header error: %s" % (arg,) if verbose: - print 'Fork %d, offset %d, length %d'%(id, offset, length) - input.seek(offset) - if length == 0: - data = '' - else: - data = input.read(length) - if len(data) != length: - raise Error, 'Short read: expected %d bytes got %d'%(length, len(data)) - if id == AS_DATAFORK: + print 'Magic: 0x%8.8x' % (magic,) + print 'Version: 0x%8.8x' % (version,) + print 'Entries: %d' % (nentry,) + if magic != AS_MAGIC: + raise Error, "Unknown AppleSingle magic number 0x%8.8x" % (magic,) + if version != AS_VERSION: + raise Error, "Unknown AppleSingle version number 0x%8.8x" % (version,) + if nentry <= 0: + raise Error, "AppleSingle file contains no forks" + headers = [fileobj.read(AS_ENTRY_LENGTH) for i in xrange(nentry)] + self.forks = [] + for hdr in headers: + try: + restype, offset, length = struct.unpack(AS_ENTRY_FORMAT, hdr) + except ValueError, arg: + raise Error, "Unpack entry error: %s" % (arg,) if verbose: - print ' (data fork)' - if not resonly: - didwork = 1 - fp = open(output, 'wb') - fp.write(data) - fp.close() - elif id == AS_RESOURCEFORK: - didwork = 1 - if verbose: - print ' (resource fork)' - if resonly: - fp = open(output, 'wb') - else: - fp = MacOS.openrf(output, 'wb') - fp.write(data) + print "Fork %d, offset %d, length %d" % (restype, offset, length) + fileobj.seek(offset) + data = fileobj.read(length) + if len(data) != length: + raise Error, "Short read: expected %d bytes got %d" % (length, len(data)) + self.forks.append((restype, data)) + if restype == AS_DATAFORK: + self.datafork = data + elif restype == AS_RESOURCEFORK: + self.resourcefork = data + + def tofile(self, path, resonly=False): + outfile = open(path, 'wb') + data = False + if resonly: + if self.resourcefork is None: + raise Error, "No resource fork found" + fp = open(path, 'wb') + fp.write(self.resourcefork) fp.close() - elif id in AS_IGNORE: - if verbose: - print ' (ignored)' + elif (self.resourcefork is None and self.datafork is None): + raise Error, "No useful forks found" else: - raise Error, 'Unknown fork type %d'%id - if not didwork: - raise Error, 'No useful forks found' + if self.datafork is not None: + fp = open(path, 'wb') + fp.write(self.datafork) + fp.close() + if self.resourcefork is not None: + fp = MacOS.openrf(path, '*wb') + fp.write(self.resourcefork) + fp.close() + +def decode(infile, outpath, resonly=False, verbose=False): + """decode(infile, outpath [, resonly=False, verbose=False]) + Creates a decoded file from an AppleSingle encoded file. + If resonly is True, then it will create a regular file at + outpath containing only the resource fork from infile. + Otherwise it will create an AppleDouble file at outpath + with the data and resource forks from infile. On platforms + without the MacOS module, it will create inpath and inpath+'.rsrc' + with the data and resource forks respectively. + + """ + if not hasattr(infile, 'read'): + if isinstance(infile, Carbon.File.Alias): + infile = infile.ResolveAlias()[0] + if isinstance(infile, (Carbon.File.FSSpec, Carbon.File.FSRef)): + infile = infile.as_pathname() + infile = open(infile, 'rb') + + as = AppleSingle(infile, verbose=verbose) + as.tofile(outpath, resonly=resonly) + def _test(): if len(sys.argv) < 3 or sys.argv[1] == '-r' and len(sys.argv) != 4: print 'Usage: applesingle.py [-r] applesinglefile decodedfile' sys.exit(1) if sys.argv[1] == '-r': - resonly = 1 + resonly = True del sys.argv[1] else: - resonly = 0 + resonly = False decode(sys.argv[1], sys.argv[2], resonly=resonly) if __name__ == '__main__': _test() - \ No newline at end of file -- cgit v0.12