| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- #
- # Copyright (C) 2008 The Android Open Source Project
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- import bz2
- import stat
- import tarfile
- import zlib
- import StringIO
- from import_ext import ImportExternal
- from error import ImportError
- class ImportTar(ImportExternal):
- """Streams a (optionally compressed) tar file from the network
- directly into a Project's Git repository.
- """
- @classmethod
- def CanAccept(cls, url):
- """Can this importer read and unpack the data stored at url?
- """
- if url.endswith('.tar.gz') or url.endswith('.tgz'):
- return True
- if url.endswith('.tar.bz2'):
- return True
- if url.endswith('.tar'):
- return True
- return False
- def _UnpackFiles(self):
- url_fd, url = self._OpenUrl()
- try:
- if url.endswith('.tar.gz') or url.endswith('.tgz'):
- tar_fd = _Gzip(url_fd)
- elif url.endswith('.tar.bz2'):
- tar_fd = _Bzip2(url_fd)
- elif url.endswith('.tar'):
- tar_fd = _Raw(url_fd)
- else:
- raise ImportError('non-tar file extension: %s' % url)
- try:
- tar = tarfile.TarFile(name = url,
- mode = 'r',
- fileobj = tar_fd)
- try:
- for entry in tar:
- mode = entry.mode
- if (mode & 0170000) == 0:
- if entry.isdir():
- mode |= stat.S_IFDIR
- elif entry.isfile() or entry.islnk(): # hard links as files
- mode |= stat.S_IFREG
- elif entry.issym():
- mode |= stat.S_IFLNK
- if stat.S_ISLNK(mode): # symlink
- data_fd = StringIO.StringIO(entry.linkname)
- data_sz = len(entry.linkname)
- elif stat.S_ISDIR(mode): # directory
- data_fd = StringIO.StringIO('')
- data_sz = 0
- else:
- data_fd = tar.extractfile(entry)
- data_sz = entry.size
- self._UnpackOneFile(mode, data_sz, entry.name, data_fd)
- finally:
- tar.close()
- finally:
- tar_fd.close()
- finally:
- url_fd.close()
- class _DecompressStream(object):
- """file like object to decompress a tar stream
- """
- def __init__(self, fd):
- self._fd = fd
- self._pos = 0
- self._buf = None
- def tell(self):
- return self._pos
- def seek(self, offset):
- d = offset - self._pos
- if d > 0:
- self.read(d)
- elif d == 0:
- pass
- else:
- raise NotImplementedError, 'seek backwards'
- def close(self):
- self._fd = None
- def read(self, size = -1):
- if not self._fd:
- raise EOFError, 'Reached EOF'
-
- r = []
- try:
- if size >= 0:
- self._ReadChunk(r, size)
- else:
- while True:
- self._ReadChunk(r, 2048)
- except EOFError:
- pass
- if len(r) == 1:
- r = r[0]
- else:
- r = ''.join(r)
- self._pos += len(r)
- return r
- def _ReadChunk(self, r, size):
- b = self._buf
- try:
- while size > 0:
- if b is None or len(b) == 0:
- b = self._Decompress(self._fd.read(2048))
- continue
- use = min(size, len(b))
- r.append(b[:use])
- b = b[use:]
- size -= use
- finally:
- self._buf = b
- def _Decompress(self, b):
- raise NotImplementedError, '_Decompress'
- class _Raw(_DecompressStream):
- """file like object for an uncompressed stream
- """
- def __init__(self, fd):
- _DecompressStream.__init__(self, fd)
- def _Decompress(self, b):
- return b
- class _Bzip2(_DecompressStream):
- """file like object to decompress a .bz2 stream
- """
- def __init__(self, fd):
- _DecompressStream.__init__(self, fd)
- self._bz = bz2.BZ2Decompressor()
- def _Decompress(self, b):
- return self._bz.decompress(b)
- _FHCRC, _FEXTRA, _FNAME, _FCOMMENT = 2, 4, 8, 16
- class _Gzip(_DecompressStream):
- """file like object to decompress a .gz stream
- """
- def __init__(self, fd):
- _DecompressStream.__init__(self, fd)
- self._z = zlib.decompressobj(-zlib.MAX_WBITS)
- magic = fd.read(2)
- if magic != '\037\213':
- raise IOError, 'Not a gzipped file'
- method = ord(fd.read(1))
- if method != 8:
- raise IOError, 'Unknown compression method'
- flag = ord(fd.read(1))
- fd.read(6)
- if flag & _FEXTRA:
- xlen = ord(fd.read(1))
- xlen += 256 * ord(fd.read(1))
- fd.read(xlen)
- if flag & _FNAME:
- while fd.read(1) != '\0':
- pass
- if flag & _FCOMMENT:
- while fd.read(1) != '\0':
- pass
- if flag & _FHCRC:
- fd.read(2)
- def _Decompress(self, b):
- return self._z.decompress(b)
|