home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / webclean / !!!python!!! / BeOpen-Python-2.0.exe / GZIP.PY < prev    next >
Encoding:
Python Source  |  2000-09-28  |  12.3 KB  |  362 lines

  1. """Functions that read and write gzipped files.
  2.  
  3. The user of the file doesn't have to worry about the compression,
  4. but random access is not allowed."""
  5.  
  6. # based on Andrew Kuchling's minigzip.py distributed with the zlib module
  7.  
  8. import string, struct, sys, time
  9. import zlib
  10. import __builtin__
  11.  
  12. FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16
  13.  
  14. READ, WRITE = 1, 2
  15.  
  16. def write32(output, value):
  17.     output.write(struct.pack("<l", value))
  18.     
  19. def write32u(output, value):
  20.     output.write(struct.pack("<L", value))
  21.  
  22. def read32(input):
  23.     return struct.unpack("<l", input.read(4))[0]
  24.  
  25. def open(filename, mode="rb", compresslevel=9):
  26.     return GzipFile(filename, mode, compresslevel)
  27.  
  28. class GzipFile:
  29.  
  30.     myfileobj = None
  31.  
  32.     def __init__(self, filename=None, mode=None, 
  33.                  compresslevel=9, fileobj=None):
  34.         if fileobj is None:
  35.             fileobj = self.myfileobj = __builtin__.open(filename, mode or 'rb')
  36.         if filename is None:
  37.             if hasattr(fileobj, 'name'): filename = fileobj.name
  38.             else: filename = ''
  39.         if mode is None:
  40.             if hasattr(fileobj, 'mode'): mode = fileobj.mode
  41.             else: mode = 'rb'
  42.  
  43.         if mode[0:1] == 'r':
  44.             self.mode = READ
  45.          # Set flag indicating start of a new member
  46.             self._new_member = 1 
  47.             self.extrabuf = ""
  48.             self.extrasize = 0
  49.             self.filename = filename
  50.  
  51.         elif mode[0:1] == 'w' or mode[0:1] == 'a':
  52.             self.mode = WRITE
  53.             self._init_write(filename)
  54.             self.compress = zlib.compressobj(compresslevel,
  55.                                              zlib.DEFLATED, 
  56.                                              -zlib.MAX_WBITS,
  57.                                              zlib.DEF_MEM_LEVEL,
  58.                                              0)
  59.         else:
  60.             raise ValueError, "Mode " + mode + " not supported"
  61.  
  62.         self.fileobj = fileobj
  63.  
  64.         if self.mode == WRITE:
  65.             self._write_gzip_header()
  66.  
  67.     def __repr__(self):
  68.         s = repr(self.fileobj)
  69.         return '<gzip ' + s[1:-1] + ' ' + hex(id(self)) + '>'
  70.  
  71.     def _init_write(self, filename):
  72.         if filename[-3:] != '.gz':
  73.             filename = filename + '.gz'
  74.         self.filename = filename
  75.         self.crc = zlib.crc32("")
  76.         self.size = 0
  77.         self.writebuf = []
  78.         self.bufsize = 0
  79.  
  80.     def _write_gzip_header(self):
  81.         self.fileobj.write('\037\213')             # magic header
  82.         self.fileobj.write('\010')                 # compression method
  83.         fname = self.filename[:-3]
  84.         flags = 0
  85.         if fname:
  86.             flags = FNAME
  87.         self.fileobj.write(chr(flags))
  88.         write32u(self.fileobj, long(time.time()))
  89.         self.fileobj.write('\002')
  90.         self.fileobj.write('\377')
  91.         if fname:
  92.             self.fileobj.write(fname + '\000')
  93.  
  94.     def _init_read(self):
  95.         self.crc = zlib.crc32("")
  96.         self.size = 0
  97.  
  98.     def _read_gzip_header(self):
  99.         magic = self.fileobj.read(2)
  100.         if magic != '\037\213':
  101.             raise IOError, 'Not a gzipped file'
  102.         method = ord( self.fileobj.read(1) )
  103.         if method != 8:
  104.             raise IOError, 'Unknown compression method'
  105.         flag = ord( self.fileobj.read(1) )
  106.         # modtime = self.fileobj.read(4)
  107.         # extraflag = self.fileobj.read(1)
  108.         # os = self.fileobj.read(1)
  109.         self.fileobj.read(6)
  110.  
  111.         if flag & FEXTRA:
  112.             # Read & discard the extra field, if present
  113.             xlen=ord(self.fileobj.read(1))              
  114.             xlen=xlen+256*ord(self.fileobj.read(1))
  115.             self.fileobj.read(xlen)
  116.         if flag & FNAME:
  117.             # Read and discard a null-terminated string containing the filename
  118.             while (1):
  119.                 s=self.fileobj.read(1)
  120.                 if not s or s=='\000': break
  121.         if flag & FCOMMENT:
  122.             # Read and discard a null-terminated string containing a comment
  123.             while (1):
  124.                 s=self.fileobj.read(1)
  125.                 if not s or s=='\000': break
  126.         if flag & FHCRC:
  127.             self.fileobj.read(2)     # Read & discard the 16-bit header CRC
  128.  
  129.  
  130.     def write(self,data):
  131.         if self.fileobj is None:
  132.             raise ValueError, "write() on closed GzipFile object"
  133.         if len(data) > 0:
  134.             self.size = self.size + len(data)
  135.             self.crc = zlib.crc32(data, self.crc)
  136.             self.fileobj.write( self.compress.compress(data) )
  137.  
  138.     def writelines(self,lines):
  139.         self.write(string.join(lines))
  140.  
  141.     def read(self, size=-1):
  142.         if self.extrasize <= 0 and self.fileobj is None:
  143.             return ''
  144.  
  145.         readsize = 1024
  146.         if size < 0:        # get the whole thing
  147.             try:
  148.                 while 1:
  149.                     self._read(readsize)
  150.                     readsize = readsize * 2
  151.             except EOFError:
  152.                 size = self.extrasize
  153.         else:               # just get some more of it
  154.             try:
  155.                 while size > self.extrasize:
  156.                     self._read(readsize)
  157.                     readsize = readsize * 2
  158.             except EOFError:
  159.                 if size > self.extrasize:
  160.                     size = self.extrasize
  161.         
  162.         chunk = self.extrabuf[:size]
  163.         self.extrabuf = self.extrabuf[size:]
  164.         self.extrasize = self.extrasize - size
  165.  
  166.         return chunk
  167.  
  168.     def _unread(self, buf):
  169.         self.extrabuf = buf + self.extrabuf
  170.         self.extrasize = len(buf) + self.extrasize
  171.  
  172.     def _read(self, size=1024):
  173.         if self.fileobj is None: raise EOFError, "Reached EOF"
  174.      
  175.         if self._new_member:
  176.             # If the _new_member flag is set, we have to
  177.             # jump to the next member, if there is one.
  178.             # 
  179.             # First, check if we're at the end of the file;
  180.             # if so, it's time to stop; no more members to read.
  181.             pos = self.fileobj.tell()   # Save current position
  182.             self.fileobj.seek(0, 2)     # Seek to end of file
  183.             if pos == self.fileobj.tell():
  184.                 self.fileobj = None
  185.                 raise EOFError, "Reached EOF"
  186.             else: 
  187.                 self.fileobj.seek( pos ) # Return to original position
  188.   
  189.             self._init_read()       
  190.             self._read_gzip_header()
  191.             self.decompress = zlib.decompressobj(-zlib.MAX_WBITS)
  192.             self._new_member = 0
  193.  
  194.         # Read a chunk of data from the file
  195.         buf = self.fileobj.read(size)
  196.  
  197.         # If the EOF has been reached, flush the decompression object
  198.         # and mark this object as finished.
  199.        
  200.         if buf == "":
  201.             uncompress = self.decompress.flush()
  202.             self._read_eof()
  203.             self.fileobj = None
  204.             self._add_read_data( uncompress )
  205.             raise EOFError, 'Reached EOF'
  206.   
  207.         uncompress = self.decompress.decompress(buf)
  208.         self._add_read_data( uncompress )
  209.  
  210.         if self.decompress.unused_data != "":
  211.             # Ending case: we've come to the end of a member in the file,
  212.             # so seek back to the start of the unused data, finish up
  213.             # this member, and read a new gzip header.
  214.             # (The number of bytes to seek back is the length of the unused
  215.             # data, minus 8 because _read_eof() will rewind a further 8 bytes)
  216.             self.fileobj.seek( -len(self.decompress.unused_data)+8, 1)
  217.  
  218.             # Check the CRC and file size, and set the flag so we read
  219.             # a new member on the next call 
  220.             self._read_eof()
  221.             self._new_member = 1        
  222.         
  223.     def _add_read_data(self, data):            
  224.         self.crc = zlib.crc32(data, self.crc)
  225.         self.extrabuf = self.extrabuf + data
  226.         self.extrasize = self.extrasize + len(data)
  227.         self.size = self.size + len(data)
  228.  
  229.     def _read_eof(self):
  230.         # We've read to the end of the file, so we have to rewind in order
  231.         # to reread the 8 bytes containing the CRC and the file size.  
  232.         # We check the that the computed CRC and size of the
  233.         # uncompressed data matches the stored values.
  234.         self.fileobj.seek(-8, 1)
  235.         crc32 = read32(self.fileobj)
  236.         isize = read32(self.fileobj)
  237.         if crc32%0x100000000L != self.crc%0x100000000L:
  238.             raise ValueError, "CRC check failed"
  239.         elif isize != self.size:
  240.             raise ValueError, "Incorrect length of data produced"
  241.           
  242.     def close(self):
  243.         if self.mode == WRITE:
  244.             self.fileobj.write(self.compress.flush())
  245.             write32(self.fileobj, self.crc)
  246.             write32(self.fileobj, self.size)
  247.             self.fileobj = None
  248.         elif self.mode == READ:
  249.             self.fileobj = None
  250.         if self.myfileobj:
  251.             self.myfileobj.close()
  252.             self.myfileobj = None
  253.  
  254.     def __del__(self):
  255.         try:
  256.             if (self.myfileobj is None and
  257.                 self.fileobj is None):
  258.                 return
  259.         except AttributeError:
  260.             return
  261.         self.close()
  262.         
  263.     def flush(self):
  264.         self.fileobj.flush()
  265.  
  266.     def seek(self):
  267.         raise IOError, 'Random access not allowed in gzip files'
  268.  
  269.     def tell(self):
  270.         raise IOError, 'I won\'t tell() you for gzip files'
  271.  
  272.     def isatty(self):
  273.         return 0
  274.  
  275.     def readline(self, size=-1):
  276.         if size < 0: size = sys.maxint
  277.         bufs = []
  278.         orig_size = size
  279.         readsize = min(100, size)    # Read from the file in small chunks
  280.         while 1:
  281.             if size == 0:
  282.                 return string.join(bufs, '') # Return resulting line
  283.  
  284.             c = self.read(readsize)
  285.             i = string.find(c, '\n')
  286.             if size is not None:
  287.                 # We set i=size to break out of the loop under two
  288.                 # conditions: 1) there's no newline, and the chunk is 
  289.                 # larger than size, or 2) there is a newline, but the
  290.                 # resulting line would be longer than 'size'.
  291.                 if i==-1 and len(c) > size: i=size-1
  292.                 elif size <= i: i = size -1
  293.  
  294.             if i >= 0 or c == '':
  295.                 bufs.append(c[:i+1])    # Add portion of last chunk
  296.                 self._unread(c[i+1:])   # Push back rest of chunk
  297.                 return string.join(bufs, '') # Return resulting line
  298.  
  299.             # Append chunk to list, decrease 'size',
  300.             bufs.append(c)
  301.             size = size - len(c)
  302.             readsize = min(size, readsize * 2)
  303.             
  304.     def readlines(self, sizehint=0):
  305.         # Negative numbers result in reading all the lines
  306.         if sizehint <= 0: sizehint = sys.maxint
  307.         L = []
  308.         while sizehint > 0:
  309.             line = self.readline()
  310.             if line == "": break
  311.             L.append( line )
  312.             sizehint = sizehint - len(line)
  313.  
  314.         return L
  315.  
  316.     def writelines(self, L):
  317.         for line in L:
  318.             self.write(line)
  319.  
  320.  
  321. def _test():
  322.     # Act like gzip; with -d, act like gunzip.
  323.     # The input file is not deleted, however, nor are any other gzip
  324.     # options or features supported.
  325.     import sys
  326.     args = sys.argv[1:]
  327.     decompress = args and args[0] == "-d"
  328.     if decompress:
  329.         args = args[1:]
  330.     if not args:
  331.         args = ["-"]
  332.     for arg in args:
  333.         if decompress:
  334.             if arg == "-":
  335.                 f = GzipFile(filename="", mode="rb", fileobj=sys.stdin)
  336.                 g = sys.stdout
  337.             else:
  338.                 if arg[-3:] != ".gz":
  339.                     print "filename doesn't end in .gz:", `arg`
  340.                     continue
  341.                 f = open(arg, "rb")
  342.                 g = __builtin__.open(arg[:-3], "wb")
  343.         else:
  344.             if arg == "-":
  345.                 f = sys.stdin
  346.                 g = GzipFile(filename="", mode="wb", fileobj=sys.stdout)
  347.             else:
  348.                 f = __builtin__.open(arg, "rb")
  349.                 g = open(arg + ".gz", "wb")
  350.         while 1:
  351.             chunk = f.read(1024)
  352.             if not chunk:
  353.                 break
  354.             g.write(chunk)
  355.         if g is not sys.stdout:
  356.             g.close()
  357.         if f is not sys.stdin:
  358.             f.close()
  359.  
  360. if __name__ == '__main__':
  361.     _test()
  362.