home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / lib / python2.6 / binhex.py < prev    next >
Encoding:
Python Source  |  2010-12-26  |  14.5 KB  |  522 lines

  1. """Macintosh binhex compression/decompression.
  2.  
  3. easy interface:
  4. binhex(inputfilename, outputfilename)
  5. hexbin(inputfilename, outputfilename)
  6. """
  7.  
  8. #
  9. # Jack Jansen, CWI, August 1995.
  10. #
  11. # The module is supposed to be as compatible as possible. Especially the
  12. # easy interface should work "as expected" on any platform.
  13. # XXXX Note: currently, textfiles appear in mac-form on all platforms.
  14. # We seem to lack a simple character-translate in python.
  15. # (we should probably use ISO-Latin-1 on all but the mac platform).
  16. # XXXX The simple routines are too simple: they expect to hold the complete
  17. # files in-core. Should be fixed.
  18. # XXXX It would be nice to handle AppleDouble format on unix
  19. # (for servers serving macs).
  20. # XXXX I don't understand what happens when you get 0x90 times the same byte on
  21. # input. The resulting code (xx 90 90) would appear to be interpreted as an
  22. # escaped *value* of 0x90. All coders I've seen appear to ignore this nicety...
  23. #
  24. import sys
  25. import os
  26. import struct
  27. import binascii
  28.  
  29. __all__ = ["binhex","hexbin","Error"]
  30.  
  31. class Error(Exception):
  32.     pass
  33.  
  34. # States (what have we written)
  35. [_DID_HEADER, _DID_DATA, _DID_RSRC] = range(3)
  36.  
  37. # Various constants
  38. REASONABLY_LARGE=32768  # Minimal amount we pass the rle-coder
  39. LINELEN=64
  40. RUNCHAR=chr(0x90)   # run-length introducer
  41.  
  42. #
  43. # This code is no longer byte-order dependent
  44.  
  45. #
  46. # Workarounds for non-mac machines.
  47. try:
  48.     from Carbon.File import FSSpec, FInfo
  49.     from MacOS import openrf
  50.  
  51.     def getfileinfo(name):
  52.         finfo = FSSpec(name).FSpGetFInfo()
  53.         dir, file = os.path.split(name)
  54.         # XXX Get resource/data sizes
  55.         fp = open(name, 'rb')
  56.         fp.seek(0, 2)
  57.         dlen = fp.tell()
  58.         fp = openrf(name, '*rb')
  59.         fp.seek(0, 2)
  60.         rlen = fp.tell()
  61.         return file, finfo, dlen, rlen
  62.  
  63.     def openrsrc(name, *mode):
  64.         if not mode:
  65.             mode = '*rb'
  66.         else:
  67.             mode = '*' + mode[0]
  68.         return openrf(name, mode)
  69.  
  70. except ImportError:
  71.     #
  72.     # Glue code for non-macintosh usage
  73.     #
  74.  
  75.     class FInfo:
  76.         def __init__(self):
  77.             self.Type = '????'
  78.             self.Creator = '????'
  79.             self.Flags = 0
  80.  
  81.     def getfileinfo(name):
  82.         finfo = FInfo()
  83.         # Quick check for textfile
  84.         fp = open(name)
  85.         data = open(name).read(256)
  86.         for c in data:
  87.             if not c.isspace() and (c<' ' or ord(c) > 0x7f):
  88.                 break
  89.         else:
  90.             finfo.Type = 'TEXT'
  91.         fp.seek(0, 2)
  92.         dsize = fp.tell()
  93.         fp.close()
  94.         dir, file = os.path.split(name)
  95.         file = file.replace(':', '-', 1)
  96.         return file, finfo, dsize, 0
  97.  
  98.     class openrsrc:
  99.         def __init__(self, *args):
  100.             pass
  101.  
  102.         def read(self, *args):
  103.             return ''
  104.  
  105.         def write(self, *args):
  106.             pass
  107.  
  108.         def close(self):
  109.             pass
  110.  
  111. class _Hqxcoderengine:
  112.     """Write data to the coder in 3-byte chunks"""
  113.  
  114.     def __init__(self, ofp):
  115.         self.ofp = ofp
  116.         self.data = ''
  117.         self.hqxdata = ''
  118.         self.linelen = LINELEN-1
  119.  
  120.     def write(self, data):
  121.         self.data = self.data + data
  122.         datalen = len(self.data)
  123.         todo = (datalen//3)*3
  124.         data = self.data[:todo]
  125.         self.data = self.data[todo:]
  126.         if not data:
  127.             return
  128.         self.hqxdata = self.hqxdata + binascii.b2a_hqx(data)
  129.         self._flush(0)
  130.  
  131.     def _flush(self, force):
  132.         first = 0
  133.         while first <= len(self.hqxdata)-self.linelen:
  134.             last = first + self.linelen
  135.             self.ofp.write(self.hqxdata[first:last]+'\n')
  136.             self.linelen = LINELEN
  137.             first = last
  138.         self.hqxdata = self.hqxdata[first:]
  139.         if force:
  140.             self.ofp.write(self.hqxdata + ':\n')
  141.  
  142.     def close(self):
  143.         if self.data:
  144.             self.hqxdata = \
  145.                  self.hqxdata + binascii.b2a_hqx(self.data)
  146.         self._flush(1)
  147.         self.ofp.close()
  148.         del self.ofp
  149.  
  150. class _Rlecoderengine:
  151.     """Write data to the RLE-coder in suitably large chunks"""
  152.  
  153.     def __init__(self, ofp):
  154.         self.ofp = ofp
  155.         self.data = ''
  156.  
  157.     def write(self, data):
  158.         self.data = self.data + data
  159.         if len(self.data) < REASONABLY_LARGE:
  160.             return
  161.         rledata = binascii.rlecode_hqx(self.data)
  162.         self.ofp.write(rledata)
  163.         self.data = ''
  164.  
  165.     def close(self):
  166.         if self.data:
  167.             rledata = binascii.rlecode_hqx(self.data)
  168.             self.ofp.write(rledata)
  169.         self.ofp.close()
  170.         del self.ofp
  171.  
  172. class BinHex:
  173.     def __init__(self, name_finfo_dlen_rlen, ofp):
  174.         name, finfo, dlen, rlen = name_finfo_dlen_rlen
  175.         if type(ofp) == type(''):
  176.             ofname = ofp
  177.             ofp = open(ofname, 'w')
  178.             if os.name == 'mac':
  179.                 fss = FSSpec(ofname)
  180.                 fss.SetCreatorType('BnHq', 'TEXT')
  181.         ofp.write('(This file must be converted with BinHex 4.0)\n\n:')
  182.         hqxer = _Hqxcoderengine(ofp)
  183.         self.ofp = _Rlecoderengine(hqxer)
  184.         self.crc = 0
  185.         if finfo is None:
  186.             finfo = FInfo()
  187.         self.dlen = dlen
  188.         self.rlen = rlen
  189.         self._writeinfo(name, finfo)
  190.         self.state = _DID_HEADER
  191.  
  192.     def _writeinfo(self, name, finfo):
  193.         nl = len(name)
  194.         if nl > 63:
  195.             raise Error, 'Filename too long'
  196.         d = chr(nl) + name + '\0'
  197.         d2 = finfo.Type + finfo.Creator
  198.  
  199.         # Force all structs to be packed with big-endian
  200.         d3 = struct.pack('>h', finfo.Flags)
  201.         d4 = struct.pack('>ii', self.dlen, self.rlen)
  202.         info = d + d2 + d3 + d4
  203.         self._write(info)
  204.         self._writecrc()
  205.  
  206.     def _write(self, data):
  207.         self.crc = binascii.crc_hqx(data, self.crc)
  208.         self.ofp.write(data)
  209.  
  210.     def _writecrc(self):
  211.         # XXXX Should this be here??
  212.         # self.crc = binascii.crc_hqx('\0\0', self.crc)
  213.         if self.crc < 0:
  214.             fmt = '>h'
  215.         else:
  216.             fmt = '>H'
  217.         self.ofp.write(struct.pack(fmt, self.crc))
  218.         self.crc = 0
  219.  
  220.     def write(self, data):
  221.         if self.state != _DID_HEADER:
  222.             raise Error, 'Writing data at the wrong time'
  223.         self.dlen = self.dlen - len(data)
  224.         self._write(data)
  225.  
  226.     def close_data(self):
  227.         if self.dlen != 0:
  228.             raise Error, 'Incorrect data size, diff=%r' % (self.rlen,)
  229.         self._writecrc()
  230.         self.state = _DID_DATA
  231.  
  232.     def write_rsrc(self, data):
  233.         if self.state < _DID_DATA:
  234.             self.close_data()
  235.         if self.state != _DID_DATA:
  236.             raise Error, 'Writing resource data at the wrong time'
  237.         self.rlen = self.rlen - len(data)
  238.         self._write(data)
  239.  
  240.     def close(self):
  241.         if self.state < _DID_DATA:
  242.             self.close_data()
  243.         if self.state != _DID_DATA:
  244.             raise Error, 'Close at the wrong time'
  245.         if self.rlen != 0:
  246.             raise Error, \
  247.                   "Incorrect resource-datasize, diff=%r" % (self.rlen,)
  248.         self._writecrc()
  249.         self.ofp.close()
  250.         self.state = None
  251.         del self.ofp
  252.  
  253. def binhex(inp, out):
  254.     """(infilename, outfilename) - Create binhex-encoded copy of a file"""
  255.     finfo = getfileinfo(inp)
  256.     ofp = BinHex(finfo, out)
  257.  
  258.     ifp = open(inp, 'rb')
  259.     # XXXX Do textfile translation on non-mac systems
  260.     while 1:
  261.         d = ifp.read(128000)
  262.         if not d: break
  263.         ofp.write(d)
  264.     ofp.close_data()
  265.     ifp.close()
  266.  
  267.     ifp = openrsrc(inp, 'rb')
  268.     while 1:
  269.         d = ifp.read(128000)
  270.         if not d: break
  271.         ofp.write_rsrc(d)
  272.     ofp.close()
  273.     ifp.close()
  274.  
  275. class _Hqxdecoderengine:
  276.     """Read data via the decoder in 4-byte chunks"""
  277.  
  278.     def __init__(self, ifp):
  279.         self.ifp = ifp
  280.         self.eof = 0
  281.  
  282.     def read(self, totalwtd):
  283.         """Read at least wtd bytes (or until EOF)"""
  284.         decdata = ''
  285.         wtd = totalwtd
  286.         #
  287.         # The loop here is convoluted, since we don't really now how
  288.         # much to decode: there may be newlines in the incoming data.
  289.         while wtd > 0:
  290.             if self.eof: return decdata
  291.             wtd = ((wtd+2)//3)*4
  292.             data = self.ifp.read(wtd)
  293.             #
  294.             # Next problem: there may not be a complete number of
  295.             # bytes in what we pass to a2b. Solve by yet another
  296.             # loop.
  297.             #
  298.             while 1:
  299.                 try:
  300.                     decdatacur, self.eof = \
  301.                             binascii.a2b_hqx(data)
  302.                     break
  303.                 except binascii.Incomplete:
  304.                     pass
  305.                 newdata = self.ifp.read(1)
  306.                 if not newdata:
  307.                     raise Error, \
  308.                           'Premature EOF on binhex file'
  309.                 data = data + newdata
  310.             decdata = decdata + decdatacur
  311.             wtd = totalwtd - len(decdata)
  312.             if not decdata and not self.eof:
  313.                 raise Error, 'Premature EOF on binhex file'
  314.         return decdata
  315.  
  316.     def close(self):
  317.         self.ifp.close()
  318.  
  319. class _Rledecoderengine:
  320.     """Read data via the RLE-coder"""
  321.  
  322.     def __init__(self, ifp):
  323.         self.ifp = ifp
  324.         self.pre_buffer = ''
  325.         self.post_buffer = ''
  326.         self.eof = 0
  327.  
  328.     def read(self, wtd):
  329.         if wtd > len(self.post_buffer):
  330.             self._fill(wtd-len(self.post_buffer))
  331.         rv = self.post_buffer[:wtd]
  332.         self.post_buffer = self.post_buffer[wtd:]
  333.         return rv
  334.  
  335.     def _fill(self, wtd):
  336.         self.pre_buffer = self.pre_buffer + self.ifp.read(wtd+4)
  337.         if self.ifp.eof:
  338.             self.post_buffer = self.post_buffer + \
  339.                 binascii.rledecode_hqx(self.pre_buffer)
  340.             self.pre_buffer = ''
  341.             return
  342.  
  343.         #
  344.         # Obfuscated code ahead. We have to take care that we don't
  345.         # end up with an orphaned RUNCHAR later on. So, we keep a couple
  346.         # of bytes in the buffer, depending on what the end of
  347.         # the buffer looks like:
  348.         # '\220\0\220' - Keep 3 bytes: repeated \220 (escaped as \220\0)
  349.         # '?\220' - Keep 2 bytes: repeated something-else
  350.         # '\220\0' - Escaped \220: Keep 2 bytes.
  351.         # '?\220?' - Complete repeat sequence: decode all
  352.         # otherwise: keep 1 byte.
  353.         #
  354.         mark = len(self.pre_buffer)
  355.         if self.pre_buffer[-3:] == RUNCHAR + '\0' + RUNCHAR:
  356.             mark = mark - 3
  357.         elif self.pre_buffer[-1] == RUNCHAR:
  358.             mark = mark - 2
  359.         elif self.pre_buffer[-2:] == RUNCHAR + '\0':
  360.             mark = mark - 2
  361.         elif self.pre_buffer[-2] == RUNCHAR:
  362.             pass # Decode all
  363.         else:
  364.             mark = mark - 1
  365.  
  366.         self.post_buffer = self.post_buffer + \
  367.             binascii.rledecode_hqx(self.pre_buffer[:mark])
  368.         self.pre_buffer = self.pre_buffer[mark:]
  369.  
  370.     def close(self):
  371.         self.ifp.close()
  372.  
  373. class HexBin:
  374.     def __init__(self, ifp):
  375.         if type(ifp) == type(''):
  376.             ifp = open(ifp)
  377.         #
  378.         # Find initial colon.
  379.         #
  380.         while 1:
  381.             ch = ifp.read(1)
  382.             if not ch:
  383.                 raise Error, "No binhex data found"
  384.             # Cater for \r\n terminated lines (which show up as \n\r, hence
  385.             # all lines start with \r)
  386.             if ch == '\r':
  387.                 continue
  388.             if ch == ':':
  389.                 break
  390.             if ch != '\n':
  391.                 dummy = ifp.readline()
  392.  
  393.         hqxifp = _Hqxdecoderengine(ifp)
  394.         self.ifp = _Rledecoderengine(hqxifp)
  395.         self.crc = 0
  396.         self._readheader()
  397.  
  398.     def _read(self, len):
  399.         data = self.ifp.read(len)
  400.         self.crc = binascii.crc_hqx(data, self.crc)
  401.         return data
  402.  
  403.     def _checkcrc(self):
  404.         filecrc = struct.unpack('>h', self.ifp.read(2))[0] & 0xffff
  405.         #self.crc = binascii.crc_hqx('\0\0', self.crc)
  406.         # XXXX Is this needed??
  407.         self.crc = self.crc & 0xffff
  408.         if filecrc != self.crc:
  409.             raise Error, 'CRC error, computed %x, read %x' \
  410.                   %(self.crc, filecrc)
  411.         self.crc = 0
  412.  
  413.     def _readheader(self):
  414.         len = self._read(1)
  415.         fname = self._read(ord(len))
  416.         rest = self._read(1+4+4+2+4+4)
  417.         self._checkcrc()
  418.  
  419.         type = rest[1:5]
  420.         creator = rest[5:9]
  421.         flags = struct.unpack('>h', rest[9:11])[0]
  422.         self.dlen = struct.unpack('>l', rest[11:15])[0]
  423.         self.rlen = struct.unpack('>l', rest[15:19])[0]
  424.  
  425.         self.FName = fname
  426.         self.FInfo = FInfo()
  427.         self.FInfo.Creator = creator
  428.         self.FInfo.Type = type
  429.         self.FInfo.Flags = flags
  430.  
  431.         self.state = _DID_HEADER
  432.  
  433.     def read(self, *n):
  434.         if self.state != _DID_HEADER:
  435.             raise Error, 'Read data at wrong time'
  436.         if n:
  437.             n = n[0]
  438.             n = min(n, self.dlen)
  439.         else:
  440.             n = self.dlen
  441.         rv = ''
  442.         while len(rv) < n:
  443.             rv = rv + self._read(n-len(rv))
  444.         self.dlen = self.dlen - n
  445.         return rv
  446.  
  447.     def close_data(self):
  448.         if self.state != _DID_HEADER:
  449.             raise Error, 'close_data at wrong time'
  450.         if self.dlen:
  451.             dummy = self._read(self.dlen)
  452.         self._checkcrc()
  453.         self.state = _DID_DATA
  454.  
  455.     def read_rsrc(self, *n):
  456.         if self.state == _DID_HEADER:
  457.             self.close_data()
  458.         if self.state != _DID_DATA:
  459.             raise Error, 'Read resource data at wrong time'
  460.         if n:
  461.             n = n[0]
  462.             n = min(n, self.rlen)
  463.         else:
  464.             n = self.rlen
  465.         self.rlen = self.rlen - n
  466.         return self._read(n)
  467.  
  468.     def close(self):
  469.         if self.rlen:
  470.             dummy = self.read_rsrc(self.rlen)
  471.         self._checkcrc()
  472.         self.state = _DID_RSRC
  473.         self.ifp.close()
  474.  
  475. def hexbin(inp, out):
  476.     """(infilename, outfilename) - Decode binhexed file"""
  477.     ifp = HexBin(inp)
  478.     finfo = ifp.FInfo
  479.     if not out:
  480.         out = ifp.FName
  481.     if os.name == 'mac':
  482.         ofss = FSSpec(out)
  483.         out = ofss.as_pathname()
  484.  
  485.     ofp = open(out, 'wb')
  486.     # XXXX Do translation on non-mac systems
  487.     while 1:
  488.         d = ifp.read(128000)
  489.         if not d: break
  490.         ofp.write(d)
  491.     ofp.close()
  492.     ifp.close_data()
  493.  
  494.     d = ifp.read_rsrc(128000)
  495.     if d:
  496.         ofp = openrsrc(out, 'wb')
  497.         ofp.write(d)
  498.         while 1:
  499.             d = ifp.read_rsrc(128000)
  500.             if not d: break
  501.             ofp.write(d)
  502.         ofp.close()
  503.  
  504.     if os.name == 'mac':
  505.         nfinfo = ofss.GetFInfo()
  506.         nfinfo.Creator = finfo.Creator
  507.         nfinfo.Type = finfo.Type
  508.         nfinfo.Flags = finfo.Flags
  509.         ofss.SetFInfo(nfinfo)
  510.  
  511.     ifp.close()
  512.  
  513. def _test():
  514.     fname = sys.argv[1]
  515.     binhex(fname, fname+'.hqx')
  516.     hexbin(fname+'.hqx', fname+'.viahqx')
  517.     #hexbin(fname, fname+'.unpacked')
  518.     sys.exit(1)
  519.  
  520. if __name__ == '__main__':
  521.     _test()
  522.