"""Macintosh binhex compression/decompression.
binhex(inputfilename, outputfilename)
hexbin(inputfilename, outputfilename)
# Jack Jansen, CWI, August 1995.
# The module is supposed to be as compatible as possible. Especially the
# easy interface should work "as expected" on any platform.
# XXXX Note: currently, textfiles appear in mac-form on all platforms.
# We seem to lack a simple character-translate in python.
# (we should probably use ISO-Latin-1 on all but the mac platform).
# XXXX The simple routines are too simple: they expect to hold the complete
# files in-core. Should be fixed.
# XXXX It would be nice to handle AppleDouble format on unix
# (for servers serving macs).
# XXXX I don't understand what happens when you get 0x90 times the same byte on
# input. The resulting code (xx 90 90) would appear to be interpreted as an
# escaped *value* of 0x90. All coders I've seen appear to ignore this nicety...
__all__
= ["binhex","hexbin","Error"]
# States (what have we written)
[_DID_HEADER
, _DID_DATA
, _DID_RSRC
] = range(3)
REASONABLY_LARGE
=32768 # Minimal amount we pass the rle-coder
RUNCHAR
=chr(0x90) # run-length introducer
# This code is no longer byte-order dependent
# Workarounds for non-mac machines.
finfo
= macfs
.FSSpec(name
).GetFInfo()
dir, file = os
.path
.split(name
)
# XXXX Get resource/data sizes
return file, finfo
, dlen
, rlen
def openrsrc(name
, *mode
):
return openrf(name
, mode
)
# Glue code for non-macintosh usage
# Quick check for textfile
data
= open(name
).read(256)
if not c
.isspace() and (c
<' ' or ord(c
) > 0x7f):
dir, file = os
.path
.split(name
)
file = file.replace(':', '-', 1)
return file, finfo
, dsize
, 0
def __init__(self
, *args
):
"""Write data to the coder in 3-byte chunks"""
self
.data
= self
.data
+ data
self
.data
= self
.data
[todo
:]
self
.hqxdata
= self
.hqxdata
+ binascii
.b2a_hqx(data
)
while first
<= len(self
.hqxdata
)-self
.linelen
:
last
= first
+ self
.linelen
self
.ofp
.write(self
.hqxdata
[first
:last
]+'\n')
self
.hqxdata
= self
.hqxdata
[first
:]
self
.ofp
.write(self
.hqxdata
+ ':\n')
self
.hqxdata
+ binascii
.b2a_hqx(self
.data
)
"""Write data to the RLE-coder in suitably large chunks"""
self
.data
= self
.data
+ data
if len(self
.data
) < REASONABLY_LARGE
:
rledata
= binascii
.rlecode_hqx(self
.data
)
rledata
= binascii
.rlecode_hqx(self
.data
)
def __init__(self
, (name
, finfo
, dlen
, rlen
), ofp
):
if type(ofp
) == type(''):
fss
= macfs
.FSSpec(ofname
)
fss
.SetCreatorType('BnHq', 'TEXT')
ofp
.write('(This file must be converted with BinHex 4.0)\n\n:')
hqxer
= _Hqxcoderengine(ofp
)
self
.ofp
= _Rlecoderengine(hqxer
)
self
._writeinfo
(name
, finfo
)
def _writeinfo(self
, name
, finfo
):
raise Error
, 'Filename too long'
d
= chr(nl
) + name
+ '\0'
d2
= finfo
.Type
+ finfo
.Creator
# Force all structs to be packed with big-endian
d3
= struct
.pack('>h', finfo
.Flags
)
d4
= struct
.pack('>ii', self
.dlen
, self
.rlen
)
self
.crc
= binascii
.crc_hqx(data
, self
.crc
)
# XXXX Should this be here??
# self.crc = binascii.crc_hqx('\0\0', self.crc)
self
.ofp
.write(struct
.pack('>h', self
.crc
))
if self
.state
!= _DID_HEADER
:
raise Error
, 'Writing data at the wrong time'
self
.dlen
= self
.dlen
- len(data
)
raise Error
, 'Incorrect data size, diff=%r' % (self
.rlen
,)
def write_rsrc(self
, data
):
if self
.state
< _DID_DATA
:
if self
.state
!= _DID_DATA
:
raise Error
, 'Writing resource data at the wrong time'
self
.rlen
= self
.rlen
- len(data
)
if self
.state
< _DID_DATA
:
if self
.state
!= _DID_DATA
:
raise Error
, 'Close at the wrong time'
"Incorrect resource-datasize, diff=%r" % (self
.rlen
,)
"""(infilename, outfilename) - Create binhex-encoded copy of a file"""
# XXXX Do textfile translation on non-mac systems
ifp
= openrsrc(inp
, 'rb')
"""Read data via the decoder in 4-byte chunks"""
def read(self
, totalwtd
):
"""Read at least wtd bytes (or until EOF)"""
# The loop here is convoluted, since we don't really now how
# much to decode: there may be newlines in the incoming data.
if self
.eof
: return decdata
data
= self
.ifp
.read(wtd
)
# Next problem: there may not be a complete number of
# bytes in what we pass to a2b. Solve by yet another
except binascii
.Incomplete
:
newdata
= self
.ifp
.read(1)
'Premature EOF on binhex file'
decdata
= decdata
+ decdatacur
wtd
= totalwtd
- len(decdata
)
if not decdata
and not self
.eof
:
raise Error
, 'Premature EOF on binhex file'
"""Read data via the RLE-coder"""
if wtd
> len(self
.post_buffer
):
self
._fill
(wtd
-len(self
.post_buffer
))
rv
= self
.post_buffer
[:wtd
]
self
.post_buffer
= self
.post_buffer
[wtd
:]
self
.pre_buffer
= self
.pre_buffer
+ self
.ifp
.read(wtd
+4)
self
.post_buffer
= self
.post_buffer
+ \
binascii
.rledecode_hqx(self
.pre_buffer
)
# Obfuscated code ahead. We have to take care that we don't
# end up with an orphaned RUNCHAR later on. So, we keep a couple
# of bytes in the buffer, depending on what the end of
# '\220\0\220' - Keep 3 bytes: repeated \220 (escaped as \220\0)
# '?\220' - Keep 2 bytes: repeated something-else
# '\220\0' - Escaped \220: Keep 2 bytes.
# '?\220?' - Complete repeat sequence: decode all
# otherwise: keep 1 byte.
mark
= len(self
.pre_buffer
)
if self
.pre_buffer
[-3:] == RUNCHAR
+ '\0' + RUNCHAR
:
elif self
.pre_buffer
[-1] == RUNCHAR
:
elif self
.pre_buffer
[-2:] == RUNCHAR
+ '\0':
elif self
.pre_buffer
[-2] == RUNCHAR
:
self
.post_buffer
= self
.post_buffer
+ \
binascii
.rledecode_hqx(self
.pre_buffer
[:mark
])
self
.pre_buffer
= self
.pre_buffer
[mark
:]
if type(ifp
) == type(''):
raise Error
, "No binhex data found"
# Cater for \r\n terminated lines (which show up as \n\r, hence
# all lines start with \r)
hqxifp
= _Hqxdecoderengine(ifp
)
self
.ifp
= _Rledecoderengine(hqxifp
)
data
= self
.ifp
.read(len)
self
.crc
= binascii
.crc_hqx(data
, self
.crc
)
filecrc
= struct
.unpack('>h', self
.ifp
.read(2))[0] & 0xffff
#self.crc = binascii.crc_hqx('\0\0', self.crc)
self
.crc
= self
.crc
& 0xffff
raise Error
, 'CRC error, computed %x, read %x' \
fname
= self
._read
(ord(len))
rest
= self
._read
(1+4+4+2+4+4)
flags
= struct
.unpack('>h', rest
[9:11])[0]
self
.dlen
= struct
.unpack('>l', rest
[11:15])[0]
self
.rlen
= struct
.unpack('>l', rest
[15:19])[0]
self
.FInfo
.Creator
= creator
if self
.state
!= _DID_HEADER
:
raise Error
, 'Read data at wrong time'
rv
= rv
+ self
._read
(n
-len(rv
))
self
.dlen
= self
.dlen
- n
if self
.state
!= _DID_HEADER
:
raise Error
, 'close_data at wrong time'
dummy
= self
._read
(self
.dlen
)
if self
.state
== _DID_HEADER
:
if self
.state
!= _DID_DATA
:
raise Error
, 'Read resource data at wrong time'
self
.rlen
= self
.rlen
- n
dummy
= self
.read_rsrc(self
.rlen
)
"""(infilename, outfilename) - Decode binhexed file"""
# XXXX Do translation on non-mac systems
d
= ifp
.read_rsrc(128000)
ofp
= openrsrc(out
, 'wb')
d
= ifp
.read_rsrc(128000)
nfinfo
.Creator
= finfo
.Creator
nfinfo
.Flags
= finfo
.Flags
fss
, ok
= macfs
.PromptGetFile('File to convert:')
fname
= fss
.as_pathname()
binhex(fname
, fname
+'.hqx')
hexbin(fname
+'.hqx', fname
+'.viahqx')
#hexbin(fname, fname+'.unpacked')
if __name__
== '__main__':