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.
This commit is contained in:
parent
5949854200
commit
3997f58a0c
|
@ -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)
|
||||
class AppleSingle(object):
|
||||
datafork = None
|
||||
resourcefork = None
|
||||
|
||||
def __init__(self, fileobj, verbose=False):
|
||||
header = fileobj.read(AS_HEADER_LENGTH)
|
||||
try:
|
||||
magic, version, dummy, nentry = struct.unpack(AS_HEADER_FORMAT, header)
|
||||
magic, version, ig, nentry = struct.unpack(AS_HEADER_FORMAT, header)
|
||||
except ValueError, arg:
|
||||
raise Error, "Unpack header error: %s"%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
|
||||
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
|
||||
raise Error, "Unknown AppleSingle magic number 0x%8.8x" % (magic,)
|
||||
if version != AS_VERSION:
|
||||
raise Error, 'Unknown AppleSingle version number 0x%8.8x'%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
|
||||
headers = [fileobj.read(AS_ENTRY_LENGTH) for i in xrange(nentry)]
|
||||
self.forks = []
|
||||
for hdr in headers:
|
||||
try:
|
||||
id, offset, length = struct.unpack(AS_ENTRY_FORMAT, hdr)
|
||||
restype, offset, length = struct.unpack(AS_ENTRY_FORMAT, hdr)
|
||||
except ValueError, arg:
|
||||
raise Error, "Unpack entry error: %s"%arg
|
||||
raise Error, "Unpack entry 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)
|
||||
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))
|
||||
if id == AS_DATAFORK:
|
||||
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)'
|
||||
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:
|
||||
fp = open(output, 'wb')
|
||||
else:
|
||||
fp = MacOS.openrf(output, 'wb')
|
||||
fp.write(data)
|
||||
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()
|
||||
|
Loading…
Reference in New Issue