~ubuntu-branches/ubuntu/saucy/boa-constructor/saucy

« back to all changes in this revision

Viewing changes to .pc/executable_script_fixup.patch/ExternalLib/tarfile.py

  • Committer: Bazaar Package Importer
  • Author(s): Charlie Smotherman
  • Date: 2011-02-15 17:48:34 UTC
  • mfrom: (7.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20110215174834-0mm22h9ffz05jaub
Tags: 0.6.1-11
* debian/source:
  - switched to using source format 3.0 (quilt).
* debian/control:
  - increase min python version to (>=2.6.6-11~).
  - after a discussion with Luca Falavigna I am taking over maintenance of 
    the package.  Added my info to Maintainers: field
  - bumped Standards-Version to 3.9.1, no changes needed.
  - removed dependency on quilt.
  - removed dependency on python-support.
  - use X-P-V instead of XS-P-V, increase version to 2.6
* debian/rules:
  - removed "--with quilt", added "--with python2".
  - changed override to "dh_python2" and corrected syntax.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# -*- coding: iso-8859-1 -*-
 
3
#-------------------------------------------------------------------
 
4
# tarfile.py
 
5
#
 
6
# Module for reading and writing .tar and tar.gz files.
 
7
#
 
8
# Needs at least Python version 2.2.
 
9
#
 
10
# Please consult the html documentation in this distribution
 
11
# for further details on how to use tarfile.
 
12
#
 
13
#-------------------------------------------------------------------
 
14
# Copyright (C) 2002 Lars Gust�bel <lars@gustaebel.de>
 
15
# All rights reserved.
 
16
#
 
17
# Permission  is  hereby granted,  free  of charge,  to  any person
 
18
# obtaining a  copy of  this software  and associated documentation
 
19
# files  (the  "Software"),  to   deal  in  the  Software   without
 
20
# restriction,  including  without limitation  the  rights to  use,
 
21
# copy, modify, merge, publish, distribute, sublicense, and/or sell
 
22
# copies  of  the  Software,  and to  permit  persons  to  whom the
 
23
# Software  is  furnished  to  do  so,  subject  to  the  following
 
24
# conditions:
 
25
#
 
26
# The above copyright  notice and this  permission notice shall  be
 
27
# included in all copies or substantial portions of the Software.
 
28
#
 
29
# THE SOFTWARE IS PROVIDED "AS  IS", WITHOUT WARRANTY OF ANY  KIND,
 
30
# EXPRESS OR IMPLIED, INCLUDING  BUT NOT LIMITED TO  THE WARRANTIES
 
31
# OF  MERCHANTABILITY,  FITNESS   FOR  A  PARTICULAR   PURPOSE  AND
 
32
# NONINFRINGEMENT.  IN  NO  EVENT SHALL  THE  AUTHORS  OR COPYRIGHT
 
33
# HOLDERS  BE LIABLE  FOR ANY  CLAIM, DAMAGES  OR OTHER  LIABILITY,
 
34
# WHETHER  IN AN  ACTION OF  CONTRACT, TORT  OR OTHERWISE,  ARISING
 
35
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 
36
# OTHER DEALINGS IN THE SOFTWARE.
 
37
#
 
38
"""Read from and write to tar format archives.
 
39
"""
 
40
 
 
41
__version__ = "$Revision: 1.1 $"
 
42
# $Source: /cvsroot/boa-constructor/boa/ExternalLib/tarfile.py,v $
 
43
 
 
44
version     = "0.6"
 
45
__author__  = "Lars Gust�bel (lars@gustaebel.de)"
 
46
__date__    = "$Date: 2003/08/11 15:28:18 $"
 
47
__cvsid__   = "$Id: tarfile.py,v 1.1 2003/08/11 15:28:18 riaan Exp $"
 
48
__credits__ = "Niels Gust�bel for his invaluable support, " \
 
49
              "Richard Townsend for endless and patient testing, " \
 
50
              "Gustavo Niemeyer for his support and his patches."
 
51
 
 
52
#---------
 
53
# Imports
 
54
#---------
 
55
import sys
 
56
import os
 
57
import __builtin__
 
58
import shutil
 
59
import stat
 
60
import errno
 
61
import time
 
62
import struct
 
63
 
 
64
try:
 
65
    import grp, pwd
 
66
except ImportError:
 
67
    grp = pwd = None
 
68
 
 
69
# We won't need this anymore in Python 2.3
 
70
#
 
71
# We import the _tarfile extension, that contains
 
72
# some useful functions to handle devices and symlinks.
 
73
# We inject them into os module, as if we were under 2.3.
 
74
#
 
75
try:
 
76
    import _tarfile
 
77
    if _tarfile.mknod is None:
 
78
        _tarfile = None
 
79
except ImportError:
 
80
    _tarfile = None
 
81
if _tarfile and not hasattr(os, "mknod"):
 
82
    os.mknod = _tarfile.mknod
 
83
if _tarfile and not hasattr(os, "major"):
 
84
    os.major = _tarfile.major
 
85
if _tarfile and not hasattr(os, "minor"):
 
86
    os.minor = _tarfile.minor
 
87
if _tarfile and not hasattr(os, "makedev"):
 
88
    os.makedev = _tarfile.makedev
 
89
if _tarfile and not hasattr(os, "lchown"):
 
90
    os.lchown = _tarfile.lchown
 
91
 
 
92
# XXX remove for release (2.3)
 
93
try:
 
94
    True
 
95
    False
 
96
except NameError:
 
97
    True  = 1
 
98
    False = 0
 
99
 
 
100
#---------------------------------------------------------
 
101
# tar constants
 
102
#---------------------------------------------------------
 
103
NUL        = "\0"               # the null character
 
104
BLOCKSIZE  = 512                # length of processing blocks
 
105
RECORDSIZE = BLOCKSIZE * 20     # length of records
 
106
MAGIC      = "ustar"            # magic tar string
 
107
VERSION    = "00"               # version number
 
108
 
 
109
LENGTH_NAME    = 100            # maximum length of a filename
 
110
LENGTH_LINK    = 100            # maximum length of a linkname
 
111
LENGTH_PREFIX  = 155            # maximum length of the prefix field
 
112
MAXSIZE_MEMBER = 077777777777L  # maximum size of a file (11 octal digits)
 
113
 
 
114
REGTYPE  = "0"                  # regular file
 
115
AREGTYPE = "\0"                 # regular file
 
116
LNKTYPE  = "1"                  # link (inside tarfile)
 
117
SYMTYPE  = "2"                  # symbolic link
 
118
CHRTYPE  = "3"                  # character special device
 
119
BLKTYPE  = "4"                  # block special device
 
120
DIRTYPE  = "5"                  # directory
 
121
FIFOTYPE = "6"                  # fifo special device
 
122
CONTTYPE = "7"                  # contiguous file
 
123
 
 
124
GNUTYPE_LONGNAME = "L"          # GNU tar extension for longnames
 
125
GNUTYPE_LONGLINK = "K"          # GNU tar extension for longlink
 
126
GNUTYPE_SPARSE   = "S"          # GNU tar extension for sparse file
 
127
 
 
128
#---------------------------------------------------------
 
129
# tarfile constants
 
130
#---------------------------------------------------------
 
131
SUPPORTED_TYPES = (REGTYPE, AREGTYPE, LNKTYPE,  # file types that tarfile
 
132
                   SYMTYPE, DIRTYPE, FIFOTYPE,  # can cope with.
 
133
                   CONTTYPE, GNUTYPE_LONGNAME,
 
134
                   GNUTYPE_LONGLINK, GNUTYPE_SPARSE,
 
135
                   CHRTYPE, BLKTYPE)
 
136
 
 
137
REGULAR_TYPES = (REGTYPE, AREGTYPE,             # file types that somehow
 
138
                 CONTTYPE, GNUTYPE_SPARSE)      # represent regular files
 
139
 
 
140
#---------------------------------------------------------
 
141
# Bits used in the mode field, values in octal.
 
142
#---------------------------------------------------------
 
143
S_IFLNK = 0120000        # symbolic link
 
144
S_IFREG = 0100000        # regular file
 
145
S_IFBLK = 0060000        # block device
 
146
S_IFDIR = 0040000        # directory
 
147
S_IFCHR = 0020000        # character device
 
148
S_IFIFO = 0010000        # fifo
 
149
 
 
150
TSUID   = 04000          # set UID on execution
 
151
TSGID   = 02000          # set GID on execution
 
152
TSVTX   = 01000          # reserved
 
153
 
 
154
TUREAD  = 0400           # read by owner
 
155
TUWRITE = 0200           # write by owner
 
156
TUEXEC  = 0100           # execute/search by owner
 
157
TGREAD  = 0040           # read by group
 
158
TGWRITE = 0020           # write by group
 
159
TGEXEC  = 0010           # execute/search by group
 
160
TOREAD  = 0004           # read by other
 
161
TOWRITE = 0002           # write by other
 
162
TOEXEC  = 0001           # execute/search by other
 
163
 
 
164
#---------------------------------------------------------
 
165
# Some useful functions
 
166
#---------------------------------------------------------
 
167
def nts(s):
 
168
    """Convert a null-terminated string buffer to a python string.
 
169
    """
 
170
    return s.split(NUL, 1)[0]
 
171
 
 
172
def calc_chksum(buf):
 
173
    """Calculate the checksum for a member's header. It's a simple addition
 
174
       of all bytes, treating the chksum field as if filled with spaces.
 
175
       buf is a 512 byte long string buffer which holds the header.
 
176
    """
 
177
    chk = 256                           # chksum field is treated as blanks,
 
178
                                        # so the initial value is 8 * ord(" ")
 
179
    for c in buf[:148]: chk += ord(c)   # sum up all bytes before chksum
 
180
    for c in buf[156:]: chk += ord(c)   # sum up all bytes after chksum
 
181
    return chk
 
182
 
 
183
def copyfileobj(src, dst, length=None):
 
184
    """Copy length bytes from fileobj src to fileobj dst.
 
185
       If length is None, copy the entire content.
 
186
    """
 
187
    if length == 0:
 
188
        return
 
189
    if length is None:
 
190
        shutil.copyfileobj(src, dst)
 
191
        return
 
192
 
 
193
    BUFSIZE = 16 * 1024
 
194
    blocks, remainder = divmod(length, BUFSIZE)
 
195
    for b in range(blocks):
 
196
        buf = src.read(BUFSIZE)
 
197
        if len(buf) < BUFSIZE:
 
198
            raise IOError, "end of file reached"
 
199
        dst.write(buf)
 
200
 
 
201
    if remainder != 0:
 
202
        buf = src.read(remainder)
 
203
        if len(buf) < remainder:
 
204
            raise IOError, "end of file reached (%d)"%(remainder-len(buf))
 
205
        dst.write(buf)
 
206
    return
 
207
 
 
208
filemode_table = (
 
209
    (S_IFLNK, "l",
 
210
     S_IFREG, "-",
 
211
     S_IFBLK, "b",
 
212
     S_IFDIR, "d",
 
213
     S_IFCHR, "c",
 
214
     S_IFIFO, "p"),
 
215
    (TUREAD,  "r"),
 
216
    (TUWRITE, "w"),
 
217
    (TUEXEC,  "x", TSUID, "S", TUEXEC|TSUID, "s"),
 
218
    (TGREAD,  "r"),
 
219
    (TGWRITE, "w"),
 
220
    (TGEXEC,  "x", TSGID, "S", TGEXEC|TSGID, "s"),
 
221
    (TOREAD,  "r"),
 
222
    (TOWRITE, "w"),
 
223
    (TOEXEC,  "x", TSVTX, "T", TOEXEC|TSVTX, "t"))
 
224
 
 
225
def filemode(mode):
 
226
    """Convert a file's mode to a string of the form
 
227
       -rwxrwxrwx.
 
228
       Used by TarFile.list()
 
229
    """
 
230
    s = ""
 
231
    for t in filemode_table:
 
232
        while True:
 
233
            if mode & t[0] == t[0]:
 
234
                s += t[1]
 
235
            elif len(t) > 2:
 
236
                t = t[2:]
 
237
                continue
 
238
            else:
 
239
                s += "-"
 
240
            break
 
241
    return s
 
242
 
 
243
if os.sep != "/":
 
244
    normpath = lambda path: os.path.normpath(path).replace(os.sep, "/")
 
245
else:
 
246
    normpath = os.path.normpath
 
247
 
 
248
class TarError(Exception):
 
249
    """General exception for extract errors"""
 
250
    pass
 
251
class ReadError(Exception):
 
252
    """Exception for unreadble tar archives"""
 
253
    pass
 
254
class CompressionError(Exception):
 
255
    """Exception for unavailable compression methods"""
 
256
    pass
 
257
class StreamError(Exception):
 
258
    """Exception for misuse of stream-like TarFiles"""
 
259
    pass
 
260
 
 
261
error = (TarError, ReadError, CompressionError, StreamError)
 
262
 
 
263
#--------------------
 
264
# exported functions
 
265
#--------------------
 
266
def open(name=None, mode="r", fileobj=None, bufsize=20*512):
 
267
    """Open a tar archive for reading, writing or appending. Return
 
268
       an appropriate TarFile class.
 
269
 
 
270
       mode:
 
271
       'r'          open for reading with transparent compression
 
272
       'r:'         open for reading exclusively uncompressed
 
273
       'r:gz'       open for reading with gzip compression
 
274
       'a'          open for appending
 
275
       'w' or 'w:'  open for writing without compression
 
276
       'w:gz'       open for writing with gzip compression
 
277
       'r|'         open an uncompressed stream of tar blocks for reading
 
278
       'r|gz'       open a gzip compressed stream of tar blocks
 
279
       'w|'         open an uncompressed stream for writing
 
280
       'w|gz'       open a gzip compressed stream for writing
 
281
    """
 
282
    if not name and not fileobj:
 
283
        raise ValueError, "nothing to open"
 
284
 
 
285
    if ":" in mode:
 
286
        filemode, comptype = mode.split(":")
 
287
        filemode = filemode or "r"
 
288
        comptype = comptype or "tar"
 
289
 
 
290
        if "%sopen" % comptype in globals():
 
291
            func = eval("%sopen" % comptype)
 
292
        else:
 
293
            raise CompressionError, "unknown compression type %r" % comptype
 
294
        return func(name, filemode, fileobj)
 
295
 
 
296
    elif "|" in mode:
 
297
        filemode, comptype = mode.split("|")
 
298
        filemode = filemode or "r"
 
299
        comptype = comptype or "tar"
 
300
 
 
301
        if filemode not in "rw":
 
302
            raise ValueError, "mode must be 'r' or 'w'"
 
303
 
 
304
        t = TarFile(name, filemode,
 
305
                    _Stream(name, filemode, comptype, fileobj, bufsize))
 
306
        t._extfileobj = False
 
307
        return t
 
308
 
 
309
    elif mode == "r":
 
310
        findcomp = lambda f: f[-4:] == "open" and f[:-4]
 
311
        comptypes = filter(findcomp, globals().keys())
 
312
        comptypes = map(findcomp, comptypes)
 
313
        for comptype in comptypes:
 
314
            func = eval("%sopen" % comptype)
 
315
            try:
 
316
                return func(name, "r", fileobj)
 
317
            except error:
 
318
                continue
 
319
        raise ReadError, "file could not be opened successfully"
 
320
 
 
321
    elif mode in "aw":
 
322
        return taropen(name, mode, fileobj)
 
323
 
 
324
    raise ValueError, "undiscernible mode"
 
325
 
 
326
def taropen(name, mode="r", fileobj=None):
 
327
    """Open uncompressed tar archive name for reading or writing.
 
328
    """
 
329
    if len(mode) > 1 or mode not in "raw":
 
330
        raise ValueError, "mode must be 'r', 'a' or 'w'"
 
331
    return TarFile(name, mode, fileobj)
 
332
 
 
333
def gzopen(name, mode="r", fileobj=None, compresslevel=9):
 
334
    """Open gzip compressed tar archive name for reading or writing.
 
335
       Appending is not allowed.
 
336
    """
 
337
    if len(mode) > 1 or mode not in "raw":
 
338
        raise ValueError, "mode must be 'r', 'a' or 'w'"
 
339
 
 
340
    try:
 
341
        import gzip
 
342
    except ImportError:
 
343
        raise CompressionError, "gzip module is not available"
 
344
 
 
345
    pre, ext = os.path.splitext(name)
 
346
    pre = os.path.basename(pre)
 
347
    if ext == ".tgz":
 
348
        ext = ".tar"
 
349
    if ext == ".gz":
 
350
        ext = ""
 
351
    tarname = pre + ext
 
352
 
 
353
    tarsrc = None
 
354
    if fileobj is None:
 
355
        if mode == 'a':
 
356
            # to emulate an tar.gz file append
 
357
            fileobj = cStringIO.StringIO(__builtin__.file(name, "rb").read())
 
358
            tarsrc = taropen(tarname, "r", gzip.GzipFile(name, "rb", 
 
359
                  compresslevel, fileobj))
 
360
 
 
361
            mode = 'w'
 
362
        
 
363
        fileobj = __builtin__.file(name, mode+"b")
 
364
 
 
365
    if mode != "r":
 
366
        name = tarname
 
367
 
 
368
    try:
 
369
        t = taropen(tarname, mode, gzip.GzipFile(tarname, mode+"b", compresslevel, fileobj))
 
370
    except IOError:
 
371
        raise ReadError, "not a gzip file"
 
372
    
 
373
    # copy existing entries when appending
 
374
    if tarsrc:
 
375
        for m in tarsrc.getmembers():
 
376
            t.addfile(m, tarsrc.extractfile(m))
 
377
 
 
378
    t._extfileobj = False
 
379
    
 
380
    return t
 
381
 
 
382
def gzopena(name, mode="a", fileobj=None, compresslevel=9):
 
383
    """Open gzip compressed tar archive name for appending.
 
384
    """
 
385
    #if len(mode) > 1 or mode not in "a":
 
386
    #    raise ValueError, "mode must be 'r', 'a' or 'w'"
 
387
 
 
388
    try:
 
389
        import gzip
 
390
    except ImportError:
 
391
        raise CompressionError, "gzip module is not available"
 
392
 
 
393
    pre, ext = os.path.splitext(name)
 
394
    pre = os.path.basename(pre)
 
395
    if ext == ".tgz":
 
396
        ext = ".tar"
 
397
    if ext == ".gz":
 
398
        ext = ""
 
399
    tarname = pre + ext
 
400
 
 
401
    if fileobj is None:
 
402
        if mode == 'a':
 
403
            # to emulate an tar.gz file append
 
404
            fileobj = __builtin__.file(name, "wb")
 
405
            
 
406
            gzipmode = 'w'
 
407
        else:
 
408
            gzipmode = mode
 
409
        
 
410
        
 
411
 
 
412
    if mode != "r":
 
413
        name = tarname
 
414
 
 
415
    try:
 
416
        t = taropen(tarname, mode, gzip.GzipFile(name, mode + "b",
 
417
                                                 compresslevel, fileobj))
 
418
    except IOError:
 
419
        raise ReadError, "not a gzip file"
 
420
    t._extfileobj = False
 
421
    return t
 
422
def bz2open(name, mode="r", fileobj=None, compresslevel=9):
 
423
    """Open bzip2 compressed tar archive name for reading or writing.
 
424
       Appending is not allowed.
 
425
    """
 
426
    if len(mode) > 1 or mode not in "rw":
 
427
        raise ValueError, "mode must be 'r' or 'w'."
 
428
 
 
429
    try:
 
430
        import bz2
 
431
    except ImportError:
 
432
        raise CompressionError, "bz2 module is not available"
 
433
 
 
434
    pre, ext = os.path.splitext(name)
 
435
    pre = os.path.basename(pre)
 
436
    if ext == ".tbz2":
 
437
        ext = ".tar"
 
438
    if ext == ".bz2":
 
439
        ext = ""
 
440
    tarname = pre + ext
 
441
 
 
442
    if fileobj is not None:
 
443
        raise ValueError, "no support for external file objects"
 
444
 
 
445
    try:
 
446
        t = taropen(tarname, mode, bz2.BZ2File(name, mode + "b"))
 
447
    except IOError:
 
448
        raise ReadError, "not a bzip2 file"
 
449
    return t
 
450
 
 
451
def is_tarfile(name):
 
452
    """Return True if name points to a tar archive that we
 
453
       are able to handle, else return False.
 
454
    """
 
455
    try:
 
456
        t = open(name)
 
457
        t.close()
 
458
        return True
 
459
    except error:
 
460
        return False
 
461
 
 
462
#---------------------------
 
463
# internal stream interface
 
464
#---------------------------
 
465
class _LowLevelFile:
 
466
    """Low-level file object. Supports reading and writing.
 
467
       It is used instead of a regular file object for streaming
 
468
       access.
 
469
    """
 
470
 
 
471
    def __init__(self, name, mode):
 
472
        mode = {
 
473
            "r": os.O_RDONLY,
 
474
            "w": os.O_WRONLY | os.O_CREAT,
 
475
        }[mode]
 
476
        if hasattr(os, "O_BINARY"):
 
477
            mode |= os.O_BINARY
 
478
        self.fd = os.open(name, mode)
 
479
 
 
480
    def close(self):
 
481
        os.close(self.fd)
 
482
 
 
483
    def read(self, size):
 
484
        return os.read(self.fd, size)
 
485
 
 
486
    def write(self, s):
 
487
        os.write(self.fd, s)
 
488
 
 
489
class _Stream:
 
490
    """Class that serves as an adapter between TarFile and
 
491
       a stream-like object.  The stream-like object only
 
492
       needs to have a read() or write() method and is accessed
 
493
       blockwise.  Use of gzip compression is possible.
 
494
       A stream-like object could be for example: sys.stdin,
 
495
       sys.stdout, a socket, a tape device etc.
 
496
 
 
497
       _Stream is intended to be used only internally.
 
498
    """
 
499
 
 
500
    def __init__(self, name, mode, type, fileobj, bufsize):
 
501
        """Construct a _Stream object.
 
502
        """
 
503
        self._extfileobj = True
 
504
        if fileobj is None:
 
505
            fileobj = _LowLevelFile(name, mode)
 
506
            self._extfileobj = False
 
507
 
 
508
        self.name    = name
 
509
        self.mode    = mode
 
510
        self.type    = type
 
511
        self.fileobj = fileobj
 
512
        self.bufsize = bufsize
 
513
        self.buf     = ""
 
514
        self.pos     = 0L
 
515
        self.closed  = False
 
516
 
 
517
        if type == "gz":
 
518
            try:
 
519
                import zlib
 
520
            except ImportError:
 
521
                raise CompressionError, "zlib module is not available"
 
522
            self.zlibmod = zlib
 
523
            self.crc = zlib.crc32("")
 
524
            if mode == "r":
 
525
                self._init_read_gz()
 
526
            else:
 
527
                self._init_write_gz()
 
528
 
 
529
    def __del__(self):
 
530
        if not self.closed:
 
531
            self.close()
 
532
 
 
533
    def _init_write_gz(self):
 
534
        """Initialize for writing with gzip compression.
 
535
        """
 
536
        zlib = self.zlibmod
 
537
        self.zlib = zlib.compressobj(9, zlib.DEFLATED,
 
538
                                        -zlib.MAX_WBITS,
 
539
                                        zlib.DEF_MEM_LEVEL,
 
540
                                        0)
 
541
        timestamp = struct.pack("<L", long(time.time()))
 
542
        self.__write("\037\213\010\010%s\002\377" % timestamp)
 
543
        if self.name.endswith(".gz"):
 
544
            self.name = self.name[:-3]
 
545
        self.__write(self.name + NUL)
 
546
 
 
547
    def write(self, s):
 
548
        """Write string s to the stream.
 
549
        """
 
550
        if self.type == "gz":
 
551
            self.pos += len(s)
 
552
            self.crc = self.zlibmod.crc32(s, self.crc)
 
553
            s = self.zlib.compress(s)
 
554
        self.__write(s)
 
555
 
 
556
    def __write(self, s):
 
557
        """Write string s to the stream if a whole new block
 
558
           is ready to be written.
 
559
        """
 
560
        self.buf += s
 
561
        while len(self.buf) > self.bufsize:
 
562
            self.fileobj.write(self.buf[:self.bufsize])
 
563
            self.buf = self.buf[self.bufsize:]
 
564
 
 
565
    def close(self):
 
566
        """Close the _Stream object. No operation should be
 
567
           done on it afterwards.
 
568
        """
 
569
        if self.closed:
 
570
            return
 
571
 
 
572
        if self.mode == "w" and self.buf:
 
573
            if self.type == "gz":
 
574
                self.buf += self.zlib.flush()
 
575
            self.fileobj.write(self.buf)
 
576
            self.buf = ""
 
577
            if self.type == "gz":
 
578
                self.fileobj.write(struct.pack("<l", self.crc))
 
579
                self.fileobj.write(struct.pack("<l", self.pos))
 
580
 
 
581
        if not self._extfileobj:
 
582
            self.fileobj.close()
 
583
 
 
584
        self.fileobj = None
 
585
 
 
586
    def _init_read_gz(self):
 
587
        """Initialize for reading a gzip compressed fileobj.
 
588
        """
 
589
        self.zlib = self.zlibmod.decompressobj(-self.zlibmod.MAX_WBITS)
 
590
        self.dbuf = ""
 
591
 
 
592
        # taken from gzip.GzipFile with some alterations
 
593
        if self.__read(2) != "\037\213":
 
594
            raise ReadError, "not a gzip file"
 
595
        if self.__read(1) != "\010":
 
596
            raise CompressionError, "unsupported compression method"
 
597
 
 
598
        flag = ord(self.__read(1))
 
599
        self.__read(6)
 
600
 
 
601
        if flag & 4:
 
602
            xlen = ord(self.__read(1)) + 256 * ord(self.__read(1))
 
603
            self.read(xlen)
 
604
        if flag & 8:
 
605
            while True:
 
606
                s = self.__read(1)
 
607
                if not s or s == NUL: break
 
608
        if flag & 16:
 
609
            while True:
 
610
                s = self.__read(1)
 
611
                if not s or s == NUL: break
 
612
        if flag & 2:
 
613
            self.__read(2)
 
614
 
 
615
    def tell(self):
 
616
        """Return the stream's file pointer position.
 
617
        """
 
618
        return self.pos
 
619
 
 
620
    def seek(self, pos=0):
 
621
        """Set the stream's file pointer to pos. Negative seeking
 
622
           is forbidden.
 
623
        """
 
624
        if pos - self.pos >= 0:
 
625
            self.read(pos - self.pos)
 
626
        else:
 
627
            raise StreamError, "seeking backwards is not allowed"
 
628
        return self.pos
 
629
 
 
630
    def read(self, size=None):
 
631
        """Return the next size number of bytes from the stream.
 
632
           If size is not defined, return all bytes of the stream
 
633
           up to EOF.
 
634
        """
 
635
        if size is None:
 
636
            s = []
 
637
            while True:
 
638
                buf = self._read(self.bufsize)
 
639
                if not buf: break
 
640
                s.append(buf)
 
641
            buf = "".join(s)
 
642
        else:
 
643
            buf = self._read(size)
 
644
        self.pos += len(buf)
 
645
        return buf
 
646
 
 
647
    def _read(self, size):
 
648
        """Return size bytes from the stream. gzip compression is
 
649
           handled here.
 
650
        """
 
651
        if self.type != "gz":
 
652
            return self.__read(size)
 
653
 
 
654
        while len(self.dbuf) < size:
 
655
            buf = self.__read(1024)
 
656
            if not buf: break
 
657
            self.dbuf += self.zlib.decompress(buf)
 
658
        buf = self.dbuf[:size]
 
659
        self.dbuf = self.dbuf[size:]
 
660
        return buf
 
661
 
 
662
    def __read(self, size):
 
663
        """Return size bytes from stream. If internal buffer is empty,
 
664
           read another block from the stream.
 
665
        """
 
666
        while len(self.buf) < size:
 
667
            buf = self.fileobj.read(self.bufsize)
 
668
            if not buf: break
 
669
            self.buf += buf
 
670
        buf = self.buf[:size]
 
671
        self.buf = self.buf[size:]
 
672
        return buf
 
673
 
 
674
#------------------
 
675
# Exported Classes
 
676
#------------------
 
677
class TarInfo:
 
678
    """Informational class which holds the details about an
 
679
       archive member given by a tar header block.
 
680
       TarInfo instances are returned by TarFile.getmember(),
 
681
       TarFile.getmembers() and TarFile.gettarinfo() and are
 
682
       usually created internally.
 
683
    """
 
684
 
 
685
    def __init__(self, name=""):
 
686
        """Construct a TarInfo instance. name is the optional name
 
687
           of the member.
 
688
        """
 
689
 
 
690
        self.name     = name       # member name (dirnames must end with '/')
 
691
        self.mode     = 0666       # file permissions
 
692
        self.uid      = 0          # user id
 
693
        self.gid      = 0          # group id
 
694
        self.size     = 0          # file size
 
695
        self.mtime    = 0          # modification time
 
696
        self.chksum   = 0          # header checksum
 
697
        self.type     = REGTYPE    # member type
 
698
        self.linkname = ""         # link name
 
699
        self.uname    = "user"     # user name
 
700
        self.gname    = "group"    # group name
 
701
        self.devmajor = 0          #-
 
702
        self.devminor = 0          #-for use with CHRTYPE and BLKTYPE
 
703
        self.prefix   = ""         # prefix to filename or holding information
 
704
                                   # about sparse files
 
705
 
 
706
        self.offset   = 0          # the tar header starts here
 
707
        self.offset_data = 0       # the optional file's data starts here
 
708
        
 
709
        # zipfile compatibility
 
710
        self.filename = name 
 
711
 
 
712
    def getheader(self):
 
713
        """Return a tar header block as a 512 byte string.
 
714
        """
 
715
        name = self.name
 
716
        if self.isdir() and name[-1:] != "/":
 
717
            name += "/"
 
718
        # The following code was contributed by Detlef Lannert.
 
719
        parts = []
 
720
        for value, fieldsize in (
 
721
                (name, 100),
 
722
                ("%07o" % (self.mode & 07777), 8),
 
723
                ("%07o" % self.uid, 8),
 
724
                ("%07o" % self.gid, 8),
 
725
                ("%011o" % self.size, 12),
 
726
                ("%011o" % self.mtime, 12),
 
727
                ("        ", 8),
 
728
                (self.type, 1),
 
729
                (self.linkname, 100),
 
730
                (MAGIC, 6),
 
731
                (VERSION, 2),
 
732
                (self.uname, 32),
 
733
                (self.gname, 32),
 
734
                ("%07o" % self.devmajor, 8),
 
735
                ("%07o" % self.devminor, 8),
 
736
                (self.prefix, 155)
 
737
                ):
 
738
            l = len(value)
 
739
            parts.append(value + (fieldsize - l) * NUL)
 
740
 
 
741
        buf = "".join(parts)
 
742
        chksum = calc_chksum(buf)
 
743
        buf = buf[:148] + "%06o\0" % chksum + buf[155:]
 
744
        buf += (512 - len(buf)) * NUL
 
745
        self.buf = buf
 
746
        return buf
 
747
 
 
748
    def isreg(self):
 
749
        return self.type in REGULAR_TYPES
 
750
    def isfile(self):
 
751
        return self.isreg()
 
752
    def isdir(self):
 
753
        return self.type == DIRTYPE
 
754
    def issym(self):
 
755
        return self.type == SYMTYPE
 
756
    def islnk(self):
 
757
        return self.type == LNKTYPE
 
758
    def ischr(self):
 
759
        return self.type == CHRTYPE
 
760
    def isblk(self):
 
761
        return self.type == BLKTYPE
 
762
    def isfifo(self):
 
763
        return self.type == FIFOTYPE
 
764
    def issparse(self):
 
765
        return self.type == GNUTYPE_SPARSE
 
766
    def isdev(self):
 
767
        return self.type in (CHRTYPE, BLKTYPE, FIFOTYPE)
 
768
# class TarInfo
 
769
 
 
770
class TarFile:
 
771
    """The TarFile Class provides an interface to tar archives.
 
772
    """
 
773
 
 
774
    debug = 0                   # May be set from 0 (no msgs) to 3 (all msgs)
 
775
 
 
776
    dereference = False         # If true, add content of linked file to the
 
777
                                # tar file, else the link.
 
778
 
 
779
    ignore_zeros = False        # If true, skips empty or invalid blocks and
 
780
                                # continues processing.
 
781
 
 
782
    errorlevel = 0              # If 0, fatal errors only appear in debug
 
783
                                # messages (if debug >= 0). If > 0, errors
 
784
                                # are passed to the caller as exceptions.
 
785
 
 
786
    posix = True                # If True, generates POSIX.1-1990-compliant
 
787
                                # archives (no GNU extensions!)
 
788
 
 
789
    def __init__(self, name=None, mode="r", fileobj=None):
 
790
        self.name = name
 
791
 
 
792
        if len(mode) > 1 or mode not in "raw":
 
793
            raise ValueError, "mode must be 'r', 'a' or 'w'"
 
794
        self._mode = mode
 
795
        self.mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode]
 
796
 
 
797
        if not fileobj:
 
798
            fileobj = __builtin__.file(self.name, self.mode)
 
799
            self._extfileobj = False
 
800
        else:
 
801
            if self.name is None and hasattr(fileobj, "name"):
 
802
                self.name = fileobj.name
 
803
            if hasattr(fileobj, "mode"):
 
804
                self.mode = fileobj.mode
 
805
            self._extfileobj = True
 
806
        self.fileobj = fileobj
 
807
 
 
808
        # Init datastructures
 
809
        self.closed      = False
 
810
        self.members     = []       # list of members as TarInfo instances
 
811
        self.membernames = []       # names of members
 
812
        self.chunks      = [0]      # chunk cache
 
813
        self._loaded     = False    # flag if all members have been read
 
814
        self.offset      = 0L       # current position in the archive file
 
815
        self.inodes      = {}       # dictionary caching the inodes of
 
816
                                    # archive members already added
 
817
 
 
818
        if self._mode == "r":
 
819
            self.firstmember = None
 
820
            self.firstmember = self.next()
 
821
 
 
822
        if self._mode == "a":
 
823
            # Move to the end of the archive,
 
824
            # before the first empty block.
 
825
            self.firstmember = None
 
826
            while True:
 
827
                try:
 
828
                    tarinfo = self.next()
 
829
                except ReadError:
 
830
                    self.fileobj.seek(0)
 
831
                    break
 
832
                if tarinfo is None:
 
833
                    self.fileobj.seek(- BLOCKSIZE, 1)
 
834
                    break
 
835
            self._loaded = True
 
836
 
 
837
    def close(self):
 
838
        """Close the TarFile instance and do some cleanup.
 
839
        """
 
840
        if self.closed:
 
841
            return
 
842
 
 
843
        if self._mode in "aw":
 
844
            self.fileobj.write(NUL * (BLOCKSIZE * 2))
 
845
            self.offset += (BLOCKSIZE * 2)
 
846
            # fill up the end with zero-blocks
 
847
            # (like option -b20 for tar does)
 
848
            blocks, remainder = divmod(self.offset, RECORDSIZE)
 
849
            if remainder > 0:
 
850
                self.fileobj.write(NUL * (RECORDSIZE - remainder))
 
851
 
 
852
        if not self._extfileobj:
 
853
            self.fileobj.close()
 
854
        self.closed = True
 
855
 
 
856
    def next(self):
 
857
        """Return the next member from the archive. Return None if the
 
858
           end of the archive is reached. Normally there is no need to
 
859
           use this method directly, because the TarFile class can be
 
860
           used as an iterator.
 
861
        """
 
862
        self._check("ra")
 
863
        if self.firstmember is not None:
 
864
            r = self.firstmember
 
865
            self.firstmember = None
 
866
            return r
 
867
 
 
868
        # Read the next block.
 
869
        self.fileobj.seek(self.chunks[-1])
 
870
        while True:
 
871
            buf = self.fileobj.read(BLOCKSIZE)
 
872
            if not buf:
 
873
                return None
 
874
            try:
 
875
                tarinfo = self._buftoinfo(buf)
 
876
            except ValueError:
 
877
                if self.ignore_zeros:
 
878
                    if buf.count(NUL) == BLOCKSIZE:
 
879
                        adj = "empty"
 
880
                    else:
 
881
                        adj = "invalid"
 
882
                    self._dbg(2, "0x%X: %s block\n" % (self.offset, adj))
 
883
                    self.offset += BLOCKSIZE
 
884
                    continue
 
885
                else:
 
886
                    # Block is empty or unreadable.
 
887
                    if self.chunks[-1] == 0:
 
888
                        # If the first block is invalid. That does not
 
889
                        # look like a tar archive we can handle.
 
890
                        raise ReadError,"empty, unreadable or compressed file"
 
891
                    return None
 
892
            break
 
893
 
 
894
        # If the TarInfo instance contains a GNUTYPE longname or longlink
 
895
        # statement, we must process this first.
 
896
        if tarinfo.type in (GNUTYPE_LONGLINK, GNUTYPE_LONGNAME):
 
897
            tarinfo = self._proc_gnulong(tarinfo, tarinfo.type)
 
898
 
 
899
        if tarinfo.isreg() and tarinfo.name[:-1] == "/":
 
900
            # some old tar programs don't know DIRTYPE
 
901
            tarinfo.type = DIRTYPE
 
902
 
 
903
        if tarinfo.issparse():
 
904
            # Sparse files need some care,
 
905
            # due to the possible extra headers.
 
906
            tarinfo.offset = self.offset
 
907
            self.offset += BLOCKSIZE
 
908
            origsize = self._proc_sparse(tarinfo)
 
909
            tarinfo.offset_data = self.offset
 
910
            blocks, remainder = divmod(tarinfo.size, BLOCKSIZE)
 
911
            if remainder:
 
912
                blocks += 1
 
913
            self.offset += blocks * BLOCKSIZE
 
914
            tarinfo.size = origsize
 
915
        else:
 
916
            tarinfo.offset = self.offset
 
917
            self.offset += BLOCKSIZE
 
918
            tarinfo.offset_data = self.offset
 
919
            if tarinfo.isreg():
 
920
                # Skip the following data blocks.
 
921
                blocks, remainder = divmod(tarinfo.size, BLOCKSIZE)
 
922
                if remainder:
 
923
                    blocks += 1
 
924
                self.offset += blocks * BLOCKSIZE
 
925
 
 
926
        self.members.append(tarinfo)
 
927
        self.membernames.append(tarinfo.name)
 
928
        self.chunks.append(self.offset)
 
929
        return tarinfo
 
930
 
 
931
    def getmember(self, name):
 
932
        """Return a TarInfo instance for member name.
 
933
        """
 
934
        self._check("r")
 
935
        if name not in self.membernames and not self._loaded:
 
936
            self._load()
 
937
        if name not in self.membernames:
 
938
            raise KeyError, "filename %r not found" % name
 
939
        return self._getmember(name)
 
940
 
 
941
    def getmembers(self):
 
942
        """Return all members in the archive as a list of TarInfo
 
943
           objects.
 
944
        """
 
945
        self._check("r")
 
946
        if not self._loaded:    # if we want to obtain a list of
 
947
            self._load()        # all members, we first have to
 
948
                                # scan the whole archive.
 
949
        return self.members
 
950
 
 
951
    def getnames(self):
 
952
        """Return all members in the archive as a list of their names.
 
953
        """
 
954
        self._check("r")
 
955
        if not self._loaded:
 
956
            self._load()
 
957
        return self.membernames
 
958
 
 
959
    def gettarinfo(self, name, arcname=None):
 
960
        """Create and return a TarInfo object that represents the existing
 
961
           physical file name. The TarInfo object and the file's data can
 
962
           be added to the TarFile using addfile(). arcname specifies the
 
963
           pathname under which the member shall be stored in the archive.
 
964
 
 
965
        """
 
966
        self._check("aw")
 
967
        # Building the name of the member in the archive.
 
968
        # Backward slashes are converted to forward slashes,
 
969
        # Absolute paths are turned to relative paths.
 
970
        if arcname is None:
 
971
            arcname = name
 
972
        arcname = normpath(arcname)
 
973
        drv, arcname = os.path.splitdrive(arcname)
 
974
        while arcname[0:1] == "/":
 
975
            arcname = arcname[1:]
 
976
 
 
977
        # Now, fill the TarInfo instance with
 
978
        # information specific for the file.
 
979
        tarinfo = TarInfo()
 
980
 
 
981
        # Use os.stat or os.lstat, depending on platform
 
982
        # and if symlinks shall be resolved.
 
983
        if hasattr(os, "lstat") and not self.dereference:
 
984
            statres = os.lstat(name)
 
985
        else:
 
986
            statres = os.stat(name)
 
987
        linkname = ""
 
988
 
 
989
        stmd = statres.st_mode
 
990
        if stat.S_ISREG(stmd):
 
991
            inode = (statres.st_ino, statres.st_dev)
 
992
            if inode in self.inodes.keys() and not self.dereference:
 
993
                # Is it a hardlink to an already
 
994
                # archived file?
 
995
                type = LNKTYPE
 
996
                linkname = self.inodes[inode]
 
997
            else:
 
998
                # The inode is added only if its valid.
 
999
                # For win32 it is always 0.
 
1000
                type = REGTYPE
 
1001
                if inode[0]:
 
1002
                    self.inodes[inode] = arcname
 
1003
        elif stat.S_ISDIR(stmd):
 
1004
            type = DIRTYPE
 
1005
            if arcname[-1:] != "/":
 
1006
                arcname += "/"
 
1007
        elif stat.S_ISFIFO(stmd):
 
1008
            type = FIFOTYPE
 
1009
        elif stat.S_ISLNK(stmd):
 
1010
            type = SYMTYPE
 
1011
            linkname = os.readlink(name)
 
1012
        elif stat.S_ISCHR(stmd):
 
1013
            type = CHRTYPE
 
1014
        elif stat.S_ISBLK(stmd):
 
1015
            type = BLKTYPE
 
1016
        else:
 
1017
            return None
 
1018
 
 
1019
        # Fill the TarInfo instance with all
 
1020
        # information we can get.
 
1021
        tarinfo.name  = arcname
 
1022
        tarinfo.mode  = stmd
 
1023
        tarinfo.uid   = statres.st_uid
 
1024
        tarinfo.gid   = statres.st_gid
 
1025
        tarinfo.size  = statres.st_size
 
1026
        tarinfo.mtime = statres.st_mtime
 
1027
        tarinfo.type  = type
 
1028
        tarinfo.linkname = linkname
 
1029
        if pwd:
 
1030
            try:
 
1031
                tarinfo.uname = pwd.getpwuid(tarinfo.uid)[0]
 
1032
            except KeyError:
 
1033
                pass
 
1034
        if grp:
 
1035
            try:
 
1036
                tarinfo.gname = grp.getgrgid(tarinfo.gid)[0]
 
1037
            except KeyError:
 
1038
                pass
 
1039
 
 
1040
        if type in (CHRTYPE, BLKTYPE):
 
1041
            if hasattr(os, "major") and hasattr(os, "minor"):
 
1042
                tarinfo.devmajor = os.major(statres.st_rdev)
 
1043
                tarinfo.devminor = os.minor(statres.st_rdev)
 
1044
        return tarinfo
 
1045
 
 
1046
    def list(self, verbose=1):
 
1047
        """Print a formatted listing of TarFile's contents
 
1048
           to sys.stdout.
 
1049
        """
 
1050
        self._check("r")
 
1051
 
 
1052
        for tarinfo in self:
 
1053
            if verbose:
 
1054
                print filemode(tarinfo.mode),
 
1055
                print tarinfo.uname + "/" + tarinfo.gname,
 
1056
                if tarinfo.ischr() or tarinfo.isblk():
 
1057
                    print "%10s" % (str(tarinfo.devmajor) + "," + str(tarinfo.devminor)),
 
1058
                else:
 
1059
                    print "%10d" % tarinfo.size,
 
1060
                print "%d-%02d-%02d %02d:%02d:%02d" \
 
1061
                      % time.localtime(tarinfo.mtime)[:6],
 
1062
 
 
1063
            print tarinfo.name,
 
1064
 
 
1065
            if verbose:
 
1066
                if tarinfo.issym():
 
1067
                    print "->", tarinfo.linkname,
 
1068
                if tarinfo.islnk():
 
1069
                    print "link to", tarinfo.linkname,
 
1070
            print
 
1071
 
 
1072
    def add(self, name, arcname=None, recursive=True):
 
1073
        """Add a file to the TarFile. Directories are added
 
1074
           recursively by default.
 
1075
        """
 
1076
        self._check("aw")
 
1077
 
 
1078
        if arcname is None:
 
1079
            arcname = name
 
1080
 
 
1081
        # Skip if somebody tries to archive the archive...
 
1082
        if self.name is not None \
 
1083
            and os.path.abspath(name) == os.path.abspath(self.name):
 
1084
            self._dbg(2, "tarfile: Skipped %r\n" % name)
 
1085
            return
 
1086
 
 
1087
        # Special case: The user wants to add the current
 
1088
        # working directory.
 
1089
        if name == ".":
 
1090
            if recursive:
 
1091
                if arcname == ".":
 
1092
                    arcname = ""
 
1093
                for f in os.listdir("."):
 
1094
                    self.add(f, os.path.join(arcname, f))
 
1095
            return
 
1096
 
 
1097
        self._dbg(1, "%s\n" % name)
 
1098
 
 
1099
        # Create a TarInfo instance from the file.
 
1100
        tarinfo = self.gettarinfo(name, arcname)
 
1101
 
 
1102
        if tarinfo is None:
 
1103
            self._dbg(1, "tarfile: Unsupported type %r\n" % name)
 
1104
            return
 
1105
 
 
1106
        # Append the tar header and data to the archive.
 
1107
        if tarinfo.isreg():
 
1108
            f = __builtin__.file(name, "rb")
 
1109
            self.addfile(tarinfo, f)
 
1110
            f.close()
 
1111
 
 
1112
        if tarinfo.type in (LNKTYPE, SYMTYPE, FIFOTYPE, CHRTYPE, BLKTYPE):
 
1113
            tarinfo.size = 0L
 
1114
            self.addfile(tarinfo)
 
1115
 
 
1116
        if tarinfo.isdir():
 
1117
            self.addfile(tarinfo)
 
1118
            if recursive:
 
1119
                for f in os.listdir(name):
 
1120
                    self.add(os.path.join(name, f), os.path.join(arcname, f))
 
1121
 
 
1122
    def addfile(self, tarinfo, fileobj=None):
 
1123
        """Read from fileobj and add the data to the TarFile.
 
1124
           File information and the number of bytes to read is
 
1125
           taken from tarinfo.
 
1126
        """
 
1127
        self._check("aw")
 
1128
 
 
1129
        tarinfo.name = normpath(tarinfo.name)
 
1130
        if tarinfo.linkname:
 
1131
            tarinfo.linkname = normpath(tarinfo.linkname)
 
1132
 
 
1133
        if tarinfo.size > MAXSIZE_MEMBER:
 
1134
            raise ValueError, "file is too large (>8GB)"
 
1135
 
 
1136
        if len(tarinfo.linkname) > LENGTH_LINK:
 
1137
            if self.posix:
 
1138
                raise ValueError, "linkname is too long (>%d)" \
 
1139
                                  % (LENGTH_LINK)
 
1140
            else:
 
1141
                self._create_gnulong(tarinfo.linkname, GNUTYPE_LONGLINK)
 
1142
                tarinfo.linkname = tarinfo.linkname[:LENGTH_LINK -1]
 
1143
                self._dbg(2, "tarfile: Created GNU tar extension LONGLINK\n")
 
1144
 
 
1145
        if len(tarinfo.name) > LENGTH_NAME:
 
1146
            if self.posix:
 
1147
                prefix = tarinfo.name[:LENGTH_PREFIX + 1]
 
1148
                while prefix and prefix[-1] != "/":
 
1149
                    prefix = prefix[:-1]
 
1150
 
 
1151
                name = tarinfo.name[len(prefix):]
 
1152
                prefix = prefix[:-1]
 
1153
 
 
1154
                if not prefix or len(name) > LENGTH_NAME:
 
1155
                    raise ValueError, "name is too long (>%d)" \
 
1156
                                      % (LENGTH_NAME)
 
1157
 
 
1158
                tarinfo.name   = name
 
1159
                tarinfo.prefix = prefix
 
1160
            else:
 
1161
                self._create_gnulong(tarinfo.name, GNUTYPE_LONGNAME)
 
1162
                tarinfo.name = tarinfo.name[:LENGTH_NAME - 1]
 
1163
                self._dbg(2, "tarfile: Created GNU tar extension LONGNAME\n")
 
1164
 
 
1165
        header = tarinfo.getheader()
 
1166
        self.fileobj.write(header)
 
1167
        self.offset += BLOCKSIZE
 
1168
 
 
1169
        # If there's data to follow, append it.
 
1170
        if fileobj is not None:
 
1171
            copyfileobj(fileobj, self.fileobj, tarinfo.size)
 
1172
            blocks, remainder = divmod(tarinfo.size, BLOCKSIZE)
 
1173
            if remainder > 0:
 
1174
                self.fileobj.write(NUL * (BLOCKSIZE - remainder))
 
1175
                blocks += 1
 
1176
            self.offset += blocks * BLOCKSIZE
 
1177
 
 
1178
    def extractfile(self, member):
 
1179
        """Extract member from the TarFile and return a file-like
 
1180
           object. member may be a name or a TarInfo object.
 
1181
        """
 
1182
        self._check("r")
 
1183
 
 
1184
        if isinstance(member, TarInfo):
 
1185
            tarinfo = member
 
1186
        else:
 
1187
            tarinfo = self.getmember(member)
 
1188
 
 
1189
        if tarinfo.isreg():
 
1190
            return _FileObject(self, tarinfo)
 
1191
 
 
1192
        elif tarinfo.type not in SUPPORTED_TYPES:
 
1193
            # If a member's type is unknown, it is treated as a
 
1194
            # regular file.
 
1195
            return _FileObject(self, tarinfo)
 
1196
 
 
1197
        elif tarinfo.islnk() or tarinfo.issym():
 
1198
            if isinstance(self.fileobj, _Stream):
 
1199
                # A small but ugly workaround for the case that someone tries
 
1200
                # to extract a (sym)link as a file-object from a non-seekable
 
1201
                # stream of tar blocks.
 
1202
                raise StreamError, "cannot extract (sym)link as file object"
 
1203
            else:
 
1204
                # A (sym)link's file object is it's target's file object.
 
1205
                return self.extractfile(self._getmember(tarinfo.linkname,
 
1206
                                                        tarinfo))
 
1207
        else:
 
1208
            # If there's no data associated with the member (directory, chrdev,
 
1209
            # blkdev, etc.), return None instead of a file object.
 
1210
            return None
 
1211
 
 
1212
    def extract(self, member, path=""):
 
1213
        """Extract member from the TarFile and write it to current
 
1214
           working directory using its full pathname. If path is
 
1215
           given, it is prepended to the pathname. member may be a
 
1216
           name or a TarInfo object.
 
1217
        """
 
1218
        self._check("r")
 
1219
 
 
1220
        if isinstance(member, TarInfo):
 
1221
            tarinfo = member
 
1222
        else:
 
1223
            tarinfo = self.getmember(member)
 
1224
 
 
1225
        self._dbg(1, tarinfo.name)
 
1226
        try:
 
1227
            self._extract_member(tarinfo, os.path.join(path, tarinfo.name))
 
1228
        except EnvironmentError, e:
 
1229
            if self.errorlevel > 0:
 
1230
                raise
 
1231
            else:
 
1232
                if e.filename is None:
 
1233
                    self._dbg(1, "\ntarfile: %s" % e.strerror)
 
1234
                else:
 
1235
                    self._dbg(1, "\ntarfile: %s %r" % (e.strerror, e.filename))
 
1236
        except TarError, e:
 
1237
            if self.errorlevel > 1:
 
1238
                raise
 
1239
            else:
 
1240
                self._dbg(1, "\ntarfile: %s" % e)
 
1241
        self._dbg(1, "\n")
 
1242
 
 
1243
    def _extract_member(self, tarinfo, targetpath):
 
1244
        """Extract the TarInfo object tarinfo to a physical
 
1245
           file called targetpath.
 
1246
        """
 
1247
        # Fetch the TarInfo instance for the given name
 
1248
        # and build the destination pathname, replacing
 
1249
        # forward slashes to platform specific separators.
 
1250
        if targetpath[-1:] == "/":
 
1251
            targetpath = targetpath[:-1]
 
1252
        targetpath = os.path.normpath(targetpath)
 
1253
 
 
1254
        # Create all upper directories.
 
1255
        upperdirs = os.path.dirname(targetpath)
 
1256
        if upperdirs and not os.path.exists(upperdirs):
 
1257
            ti = TarInfo()
 
1258
            ti.name  = upperdirs
 
1259
            ti.type  = DIRTYPE
 
1260
            ti.mode  = 0777
 
1261
            ti.mtime = tarinfo.mtime
 
1262
            ti.uid   = tarinfo.uid
 
1263
            ti.gid   = tarinfo.gid
 
1264
            ti.uname = tarinfo.uname
 
1265
            ti.gname = tarinfo.gname
 
1266
            try:
 
1267
                self._extract_member(ti, ti.name)
 
1268
            except:
 
1269
                pass
 
1270
 
 
1271
        if tarinfo.isreg():
 
1272
            self._makefile(tarinfo, targetpath)
 
1273
        elif tarinfo.isdir():
 
1274
            self._makedir(tarinfo, targetpath)
 
1275
        elif tarinfo.isfifo():
 
1276
            self._makefifo(tarinfo, targetpath)
 
1277
        elif tarinfo.ischr() or tarinfo.isblk():
 
1278
            self._makedev(tarinfo, targetpath)
 
1279
        elif tarinfo.islnk() or tarinfo.issym():
 
1280
            self._makelink(tarinfo, targetpath)
 
1281
        else:
 
1282
            self._makefile(tarinfo, targetpath)
 
1283
            if tarinfo.type not in SUPPORTED_TYPES:
 
1284
                self._dbg(1, "\ntarfile: Unknown file type %r, " \
 
1285
                             "extracted as regular file." % tarinfo.type)
 
1286
 
 
1287
        if not tarinfo.issym():
 
1288
            self._chown(tarinfo, targetpath)
 
1289
            self._chmod(tarinfo, targetpath)
 
1290
            self._utime(tarinfo, targetpath)
 
1291
 
 
1292
    def _makedir(self, tarinfo, targetpath):
 
1293
        """Make a directory called targetpath from tarinfo.
 
1294
        """
 
1295
        try:
 
1296
            os.mkdir(targetpath)
 
1297
        except EnvironmentError, e:
 
1298
            if e.errno != errno.EEXIST:
 
1299
                raise
 
1300
 
 
1301
    def _makefile(self, tarinfo, targetpath):
 
1302
        """Make a file called targetpath from tarinfo.
 
1303
        """
 
1304
        source = self.extractfile(tarinfo)
 
1305
        target = __builtin__.file(targetpath, "wb")
 
1306
        copyfileobj(source, target)
 
1307
        source.close()
 
1308
        target.close()
 
1309
 
 
1310
    def _makefifo(self, tarinfo, targetpath):
 
1311
        """Make a fifo called targetpath from tarinfo.
 
1312
        """
 
1313
        if hasattr(os, "mkfifo"):
 
1314
            os.mkfifo(targetpath)
 
1315
        else:
 
1316
            raise TarError, "fifo not supported by system"
 
1317
 
 
1318
    def _makedev(self, tarinfo, targetpath):
 
1319
        """Make a character or block device called targetpath
 
1320
           from tarinfo.
 
1321
        """
 
1322
        if not hasattr(os, "mknod"):
 
1323
            raise TarError, "special devices not supported by system"
 
1324
 
 
1325
        mode = tarinfo.mode
 
1326
        if tarinfo.isblk():
 
1327
            mode |= stat.S_IFBLK
 
1328
        else:
 
1329
            mode |= stat.S_IFCHR
 
1330
 
 
1331
        # XXX This if statement should go away when
 
1332
        # python-2.3a0-devicemacros patch succeeds.
 
1333
        if hasattr(os, "makedev"):
 
1334
            os.mknod(targetpath, mode,
 
1335
                     os.makedev(tarinfo.devmajor, tarinfo.devminor))
 
1336
        else:
 
1337
            os.mknod(targetpath, mode,
 
1338
                     tarinfo.devmajor, tarinfo.devminor)
 
1339
 
 
1340
    def _makelink(self, tarinfo, targetpath):
 
1341
        """Make a (symbolic) link called targetpath from tarinfo.
 
1342
           If it cannot be created (platform limitation), we try
 
1343
           to make a copy of the referenced file instead of a link.
 
1344
        """
 
1345
        linkpath = tarinfo.linkname
 
1346
        self._dbg(1, " -> %s" % linkpath)
 
1347
        try:
 
1348
            if tarinfo.issym():
 
1349
                os.symlink(linkpath, targetpath)
 
1350
            else:
 
1351
                os.link(linkpath, targetpath)
 
1352
        except AttributeError:
 
1353
            if tarinfo.issym():
 
1354
                linkpath = os.path.join(os.path.dirname(tarinfo.name),
 
1355
                                        linkpath)
 
1356
                linkpath = normpath(linkpath)
 
1357
 
 
1358
            try:
 
1359
                self._extract_member(self.getmember(linkpath), targetpath)
 
1360
            except (EnvironmentError, KeyError), e:
 
1361
                linkpath = os.path.normpath(linkpath)
 
1362
                try:
 
1363
                    shutil.copy2(linkpath, targetpath)
 
1364
                except EnvironmentError, e:
 
1365
                    raise IOError, "link could not be created"
 
1366
 
 
1367
    def _chown(self, tarinfo, targetpath):
 
1368
        """Set owner of targetpath according to tarinfo.
 
1369
        """
 
1370
        if pwd and os.geteuid() == 0:
 
1371
            # We have to be root to do so.
 
1372
            try:
 
1373
                g = grp.getgrnam(tarinfo.gname)[2]
 
1374
            except KeyError:
 
1375
                try:
 
1376
                    g = grp.getgrgid(tarinfo.gid)[2]
 
1377
                except KeyError:
 
1378
                    g = os.getgid()
 
1379
            try:
 
1380
                u = pwd.getpwnam(tarinfo.uname)[2]
 
1381
            except KeyError:
 
1382
                try:
 
1383
                    u = pwd.getpwuid(tarinfo.uid)[2]
 
1384
                except KeyError:
 
1385
                    u = os.getuid()
 
1386
            try:
 
1387
                if tarinfo.issym() and hasattr(os, "lchown"):
 
1388
                    os.lchown(targetpath, u, g)
 
1389
                else:
 
1390
                    os.chown(targetpath, u, g)
 
1391
            except EnvironmentError, e:
 
1392
                raise TarError, "could not change owner"
 
1393
 
 
1394
    def _chmod(self, tarinfo, targetpath):
 
1395
        """Set file permissions of targetpath according to tarinfo.
 
1396
        """
 
1397
        try:
 
1398
            os.chmod(targetpath, tarinfo.mode)
 
1399
        except EnvironmentError, e:
 
1400
            raise TarError, "could not change mode"
 
1401
 
 
1402
    def _utime(self, tarinfo, targetpath):
 
1403
        """Set modification time of targetpath according to tarinfo.
 
1404
        """
 
1405
        try:
 
1406
            os.utime(targetpath, (tarinfo.mtime, tarinfo.mtime))
 
1407
        except EnvironmentError, e:
 
1408
            raise TarError, "could not change modification time"
 
1409
 
 
1410
    def _getmember(self, name, tarinfo=None):
 
1411
        """Find an archive member by name from bottom to top.
 
1412
           If tarinfo is given, it is used as the starting point.
 
1413
        """
 
1414
        if tarinfo is None:
 
1415
            end = len(self.members)
 
1416
        else:
 
1417
            end = self.members.index(tarinfo)
 
1418
 
 
1419
        for i in xrange(end - 1, -1, -1):
 
1420
            if name == self.membernames[i]:
 
1421
                return self.members[i]
 
1422
 
 
1423
    def _load(self):
 
1424
        """Read through the entire archive file and look for readable
 
1425
           members.
 
1426
        """
 
1427
        while True:
 
1428
            tarinfo = self.next()
 
1429
            if tarinfo is None:
 
1430
                break
 
1431
        self._loaded = True
 
1432
 
 
1433
    def _check(self, mode):
 
1434
        """Check if TarFile is still open, and if the operation's mode
 
1435
           corresponds to TarFile's mode.
 
1436
        """
 
1437
        if self.fileobj is None:
 
1438
            raise IOError, "%s is closed" % self.__class__.__name__
 
1439
        if self._mode not in mode:
 
1440
            raise IOError, "bad operation for mode %r" % self._mode
 
1441
 
 
1442
    def __iter__(self):
 
1443
        """Provide an iterator object.
 
1444
        """
 
1445
        if self._loaded:
 
1446
            return iter(self.members)
 
1447
        else:
 
1448
            return TarIter(self)
 
1449
 
 
1450
    def _buftoinfo(self, buf):
 
1451
        """Transform a 512 byte block to a TarInfo object.
 
1452
        """
 
1453
        tarinfo = TarInfo()
 
1454
        tarinfo.name = nts(buf[0:100])
 
1455
        tarinfo.mode = int(buf[100:108], 8)
 
1456
        tarinfo.uid = int(buf[108:116],8)
 
1457
        tarinfo.gid = int(buf[116:124],8)
 
1458
        tarinfo.size = long(buf[124:136], 8)
 
1459
        tarinfo.mtime = long(buf[136:148], 8)
 
1460
        tarinfo.chksum = int(buf[148:156], 8)
 
1461
        tarinfo.type = buf[156:157]
 
1462
        tarinfo.linkname = nts(buf[157:257])
 
1463
        tarinfo.uname = nts(buf[265:297])
 
1464
        tarinfo.gname = nts(buf[297:329])
 
1465
        try:
 
1466
            tarinfo.devmajor = int(buf[329:337], 8)
 
1467
            tarinfo.devminor = int(buf[337:345], 8)
 
1468
        except ValueError:
 
1469
            tarinfo.devmajor = tarinfo.devmajor = 0
 
1470
 
 
1471
        prefix = buf[345:500]
 
1472
        while prefix and prefix[-1] == NUL:
 
1473
            prefix = prefix[:-1]
 
1474
        if len(prefix.split(NUL)) == 1:
 
1475
            tarinfo.prefix = prefix
 
1476
            tarinfo.name = normpath(os.path.join(tarinfo.prefix, tarinfo.name))
 
1477
        else:
 
1478
            tarinfo.prefix = buf[345:500]
 
1479
 
 
1480
        if tarinfo.chksum != calc_chksum(buf):
 
1481
            if self:
 
1482
                self._dbg(1, "tarfile: Bad Checksum\n")
 
1483
        return tarinfo
 
1484
 
 
1485
    def _proc_gnulong(self, tarinfo, type):
 
1486
        """Evaluate the blocks that hold a GNU longname
 
1487
           or longlink member.
 
1488
        """
 
1489
        name = None
 
1490
        linkname = None
 
1491
        buf = ""
 
1492
        count = tarinfo.size
 
1493
        while count > 0:
 
1494
            block = self.fileobj.read(BLOCKSIZE)
 
1495
            buf += block
 
1496
            self.offset += BLOCKSIZE
 
1497
            count -= BLOCKSIZE
 
1498
 
 
1499
        if type == GNUTYPE_LONGNAME:
 
1500
            name = nts(buf)
 
1501
        if type == GNUTYPE_LONGLINK:
 
1502
            linkname = nts(buf)
 
1503
 
 
1504
        buf = self.fileobj.read(BLOCKSIZE)
 
1505
        tarinfo = self._buftoinfo(buf)
 
1506
        if name is not None:
 
1507
            tarinfo.name = name
 
1508
        if linkname is not None:
 
1509
            tarinfo.linkname = linkname
 
1510
        self.offset += BLOCKSIZE
 
1511
        return tarinfo
 
1512
 
 
1513
    def _create_gnulong(self, name, type):
 
1514
        """Write a GNU longname/longlink member to the TarFile.
 
1515
           It consists of an extended tar header, with the length
 
1516
           of the longname as size, followed by data blocks,
 
1517
           which contain the longname as a null terminated string.
 
1518
        """
 
1519
        tarinfo = TarInfo()
 
1520
        tarinfo.name = "././@LongLink"
 
1521
        tarinfo.type = type
 
1522
        tarinfo.mode = 0
 
1523
        tarinfo.size = len(name)
 
1524
 
 
1525
        # write extended header
 
1526
        self.fileobj.write(tarinfo.getheader())
 
1527
        # write name blocks
 
1528
        self.fileobj.write(name)
 
1529
        blocks, remainder = divmod(tarinfo.size, BLOCKSIZE)
 
1530
        if remainder > 0:
 
1531
            self.fileobj.write(NUL * (BLOCKSIZE - remainder))
 
1532
            blocks += 1
 
1533
        self.offset += blocks * BLOCKSIZE
 
1534
 
 
1535
    def _proc_sparse(self, tarinfo):
 
1536
        """Analyze a GNU sparse header plus extra headers.
 
1537
        """
 
1538
        buf = tarinfo.getheader()
 
1539
        sp = _ringbuffer()
 
1540
        pos = 386
 
1541
        lastpos = 0L
 
1542
        realpos = 0L
 
1543
        # There are 4 possible sparse structs in the
 
1544
        # first header.
 
1545
        for i in range(4):
 
1546
            try:
 
1547
                offset = int(buf[pos:pos + 12], 8)
 
1548
                numbytes = int(buf[pos + 12:pos + 24], 8)
 
1549
            except ValueError:
 
1550
                break
 
1551
            if offset > lastpos:
 
1552
                sp.append(_hole(lastpos, offset - lastpos))
 
1553
            sp.append(_data(offset, numbytes, realpos))
 
1554
            realpos += numbytes
 
1555
            lastpos = offset + numbytes
 
1556
            pos += 24
 
1557
 
 
1558
        isextended = ord(buf[482])
 
1559
        origsize = int(buf[483:495], 8)
 
1560
 
 
1561
        # If the isextended flag is given,
 
1562
        # there are extra headers to process.
 
1563
        while isextended == 1:
 
1564
            buf = self.fileobj.read(BLOCKSIZE)
 
1565
            self.offset += BLOCKSIZE
 
1566
            pos = 0
 
1567
            for i in range(21):
 
1568
                try:
 
1569
                    offset = int(buf[pos:pos + 12], 8)
 
1570
                    numbytes = int(buf[pos + 12:pos + 24], 8)
 
1571
                except ValueError:
 
1572
                    break
 
1573
                if offset > lastpos:
 
1574
                    sp.append(_hole(lastpos, offset - lastpos))
 
1575
                sp.append(_data(offset, numbytes, realpos))
 
1576
                realpos += numbytes
 
1577
                lastpos = offset + numbytes
 
1578
                pos += 24
 
1579
            isextended = ord(buf[504])
 
1580
 
 
1581
        if lastpos < origsize:
 
1582
            sp.append(_hole(lastpos, origsize - lastpos))
 
1583
 
 
1584
        tarinfo.sparse = sp
 
1585
        return origsize
 
1586
 
 
1587
    def _dbg(self, level, msg):
 
1588
        if level <= self.debug:
 
1589
            sys.stdout.write(msg)
 
1590
# class TarFile
 
1591
 
 
1592
class TarIter:
 
1593
    """Iterator Class.
 
1594
 
 
1595
       for tarinfo in TarFile(...):
 
1596
           suite...
 
1597
    """
 
1598
 
 
1599
    def __init__(self, tarfile):
 
1600
        """Construct a TarIter instance.
 
1601
        """
 
1602
        self.tarfile = tarfile
 
1603
    def __iter__(self):
 
1604
        """Return iterator object.
 
1605
        """
 
1606
        return self
 
1607
    def next(self):
 
1608
        """Return the next item using TarFile's next() method.
 
1609
           When all members have been read, set TarFile as _loaded.
 
1610
        """
 
1611
        tarinfo = self.tarfile.next()
 
1612
        if not tarinfo:
 
1613
            self.tarfile._loaded = True
 
1614
            raise StopIteration
 
1615
        return tarinfo
 
1616
# class TarIter
 
1617
 
 
1618
# Helper classes for sparse file support
 
1619
class _section:
 
1620
    """Base class for _data and _hole.
 
1621
    """
 
1622
    def __init__(self, offset, size):
 
1623
        self.offset = offset
 
1624
        self.size = size
 
1625
    def __contains__(self, offset):
 
1626
        return self.offset <= offset < self.offset + self.size
 
1627
 
 
1628
class _data(_section):
 
1629
    """Represent a data section in a sparse file.
 
1630
    """
 
1631
    def __init__(self, offset, size, realpos):
 
1632
        _section.__init__(self, offset, size)
 
1633
        self.realpos = realpos
 
1634
 
 
1635
class _hole(_section):
 
1636
    """Represent a hole section in a sparse file.
 
1637
    """
 
1638
    pass
 
1639
 
 
1640
class _ringbuffer(list):
 
1641
    """Ringbuffer class which increases performance
 
1642
       over a regular list.
 
1643
    """
 
1644
    def __init__(self):
 
1645
        self.idx = 0
 
1646
    def find(self, offset):
 
1647
        idx = self.idx
 
1648
        while True:
 
1649
            item = self[idx]
 
1650
            if offset in item:
 
1651
                break
 
1652
            idx += 1
 
1653
            if idx == len(self):
 
1654
                idx = 0
 
1655
            if idx == self.idx:
 
1656
                # End of File
 
1657
                return None
 
1658
        self.idx = idx
 
1659
        return item
 
1660
 
 
1661
class _FileObject:
 
1662
    """File-like object for reading an archive member.
 
1663
       Is returned by TarFile.extractfile(). Support for
 
1664
       sparse files included.
 
1665
    """
 
1666
 
 
1667
    def __init__(self, tarfile, tarinfo):
 
1668
        self.fileobj = tarfile.fileobj
 
1669
        self.name    = tarinfo.name
 
1670
        self.mode    = "r"
 
1671
        self.closed  = False
 
1672
        self.offset  = tarinfo.offset_data
 
1673
        self.size    = tarinfo.size
 
1674
        self.pos     = 0L
 
1675
        self.linebuffer = ""
 
1676
        if tarinfo.issparse():
 
1677
            self.sparse = tarinfo.sparse
 
1678
            self.read = self._readsparse
 
1679
        else:
 
1680
            self.read = self._readnormal
 
1681
 
 
1682
    def readline(self, size=-1):
 
1683
        """Read a line with approx. size. If size is negative,
 
1684
           read a whole line. readline() and read() must not
 
1685
           be mixed up (!).
 
1686
        """
 
1687
        if size < 0:
 
1688
            size = sys.maxint
 
1689
 
 
1690
        nl = self.linebuffer.find("\n")
 
1691
        if nl >= 0:
 
1692
            nl = min(nl, size)
 
1693
        else:
 
1694
            size -= len(self.linebuffer)
 
1695
            while nl < 0:
 
1696
                buf = self.read(min(size, 100))
 
1697
                if not buf:
 
1698
                    break
 
1699
                self.linebuffer += buf
 
1700
                size -= len(buf)
 
1701
                if size <= 0:
 
1702
                    break
 
1703
                nl = self.linebuffer.find("\n")
 
1704
            if nl == -1:
 
1705
                s = self.linebuffer
 
1706
                self.linebuffer = ""
 
1707
                return s
 
1708
        buf = self.linebuffer[:nl]
 
1709
        self.linebuffer = self.linebuffer[nl + 1:]
 
1710
        while buf[-1:] == "\r":
 
1711
            buf = buf[:-1]
 
1712
        return buf + "\n"
 
1713
 
 
1714
    def readlines(self):
 
1715
        """Return a list with all (following) lines.
 
1716
        """
 
1717
        result = []
 
1718
        while True:
 
1719
            line = self.readline()
 
1720
            if not line: break
 
1721
            result.append(line)
 
1722
        return result
 
1723
 
 
1724
    def _readnormal(self, size=None):
 
1725
        """Read operation for regular files.
 
1726
        """
 
1727
        if self.closed:
 
1728
            raise ValueError, "file is closed"
 
1729
        self.fileobj.seek(self.offset + self.pos)
 
1730
        bytesleft = self.size - self.pos
 
1731
        if size is None:
 
1732
            bytestoread = bytesleft
 
1733
        else:
 
1734
            bytestoread = min(size, bytesleft)
 
1735
        self.pos += bytestoread
 
1736
        return self.fileobj.read(bytestoread)
 
1737
 
 
1738
    def _readsparse(self, size=None):
 
1739
        """Read operation for sparse files.
 
1740
        """
 
1741
        if self.closed:
 
1742
            raise ValueError, "file is closed"
 
1743
 
 
1744
        if size is None:
 
1745
            size = self.size - self.pos
 
1746
 
 
1747
        data = ""
 
1748
        while size > 0:
 
1749
            buf = self._readsparsesection(size)
 
1750
            if not buf:
 
1751
                break
 
1752
            size -= len(buf)
 
1753
            data += buf
 
1754
        return data
 
1755
 
 
1756
    def _readsparsesection(self, size):
 
1757
        """Read a single section of a sparse file.
 
1758
        """
 
1759
        section = self.sparse.find(self.pos)
 
1760
 
 
1761
        if section is None:
 
1762
            return ""
 
1763
 
 
1764
        toread = min(size, section.offset + section.size - self.pos)
 
1765
        if isinstance(section, _data):
 
1766
            realpos = section.realpos + self.pos - section.offset
 
1767
            self.pos += toread
 
1768
            self.fileobj.seek(self.offset + realpos)
 
1769
            return self.fileobj.read(toread)
 
1770
        else:
 
1771
            self.pos += toread
 
1772
            return NUL * toread
 
1773
 
 
1774
    def tell(self):
 
1775
        """Return the current file position.
 
1776
        """
 
1777
        return self.pos
 
1778
 
 
1779
    def seek(self, pos, whence=0):
 
1780
        """Seek to a position in the file.
 
1781
        """
 
1782
        self.linebuffer = ""
 
1783
        if whence == 0:
 
1784
            self.pos = min(max(pos, 0), self.size)
 
1785
        if whence == 1:
 
1786
            if pos < 0:
 
1787
                self.pos = max(self.pos + pos, 0)
 
1788
            else:
 
1789
                self.pos = min(self.pos + pos, self.size)
 
1790
        if whence == 2:
 
1791
            self.pos = max(min(self.size + pos, self.size), 0)
 
1792
 
 
1793
    def close(self):
 
1794
        """Close the file object.
 
1795
        """
 
1796
        self.closed = True
 
1797
#class _FileObject
 
1798
 
 
1799
#---------------------------------------------
 
1800
# zipfile compatible TarFile class
 
1801
#
 
1802
# for details consult zipfile's documentation
 
1803
#---------------------------------------------
 
1804
import cStringIO
 
1805
 
 
1806
TAR_PLAIN = 0           # zipfile.ZIP_STORED
 
1807
TAR_GZIPPED = 8         # zipfile.ZIP_DEFLATED
 
1808
class TarFileCompat:
 
1809
    """TarFile class compatible with standard module zipfile's
 
1810
       ZipFile class.
 
1811
    """
 
1812
    def __init__(self, file, mode="r", compression=TAR_PLAIN):
 
1813
        if not isinstance(file, (type(''), type(u''))) and hasattr(file, 'read'):
 
1814
            fileobj = file
 
1815
            file = ''
 
1816
        else:
 
1817
            fileobj = None
 
1818
        
 
1819
        self.compression = compression
 
1820
       
 
1821
        if compression == TAR_PLAIN:
 
1822
            self.tarfile = open(file, mode, fileobj=fileobj)
 
1823
        elif compression == TAR_GZIPPED:
 
1824
            if mode == 'a':
 
1825
                self.tarfile = gzopen(file, mode, fileobj=fileobj)
 
1826
            else:
 
1827
                self.tarfile = gzopen(file, mode, fileobj=fileobj)
 
1828
        else:
 
1829
            raise ValueError, "unknown compression constant"
 
1830
        if mode[0:1] == "r":
 
1831
            import time
 
1832
            members = self.tarfile.getmembers()
 
1833
            for i in range(len(members)):
 
1834
                m = members[i]
 
1835
                m.filename = m.name
 
1836
                m.file_size = m.size
 
1837
                m.date_time = time.gmtime(m.mtime)[:6]
 
1838
 
 
1839
    def namelist(self):
 
1840
        return map(lambda m: m.name, self.infolist())
 
1841
    def infolist(self):
 
1842
        return filter(lambda m: m.type in REGULAR_TYPES,
 
1843
                      self.tarfile.getmembers())
 
1844
    def printdir(self):
 
1845
        self.tarfile.list()
 
1846
    def testzip(self):
 
1847
        return
 
1848
    def getinfo(self, name):
 
1849
        return self.tarfile.getmember(name)
 
1850
    def read(self, name):
 
1851
        return self.tarfile.extractfile(self.tarfile.getmember(name)).read()
 
1852
    def write(self, filename, arcname=None, compress_type=None):
 
1853
        self.tarfile.add(filename, arcname)
 
1854
    def writestr(self, zinfo, bytes):
 
1855
        import calendar
 
1856
        zinfo.name = zinfo.filename
 
1857
        zinfo.size = zinfo.file_size
 
1858
        zinfo.mtime = calendar.timegm(zinfo.date_time)
 
1859
        self.tarfile.addfile(zinfo, cStringIO.StringIO(bytes))
 
1860
    def close(self):
 
1861
        self.tarfile.close()
 
1862
#class TarFileCompat
 
1863
 
 
1864
if __name__ == "__main__":
 
1865
    # a "light-weight" implementation of GNUtar ;-)
 
1866
    usage = """
 
1867
Usage: %s [options] [files]
 
1868
 
 
1869
-h          display this help message
 
1870
 
 
1871
-c          create a tarfile
 
1872
 
 
1873
-r          append to an existing archive
 
1874
 
 
1875
-x          extract archive
 
1876
 
 
1877
-t          list archive contents
 
1878
 
 
1879
-f FILENAME use archive FILENAME, else STDOUT (-c)
 
1880
 
 
1881
-z          filter archive through gzip
 
1882
 
 
1883
-C DIRNAME  with opt -x:     extract to directory DIRNAME
 
1884
            with opt -c, -r: put files to archive under DIRNAME
 
1885
 
 
1886
-v          verbose output
 
1887
 
 
1888
-q          quiet
 
1889
 
 
1890
--posix     create a POSIX 1003.1-1990 compliant archive
 
1891
 
 
1892
wildcards *, ?, [seq], [!seq] are accepted.
 
1893
    """ % sys.argv[0]
 
1894
 
 
1895
    import getopt, glob
 
1896
    try:
 
1897
        opts, args = getopt.getopt(sys.argv[1:], "htcxrzjf:C:vq", ("posix",))
 
1898
    except getopt.GetoptError, e:
 
1899
        print
 
1900
        print "ERROR:", e
 
1901
        print usage
 
1902
        sys.exit(0)
 
1903
 
 
1904
    file  = None
 
1905
    mode  = None
 
1906
    dir   = None
 
1907
    comp  = ""
 
1908
    debug = 0
 
1909
    posix = False
 
1910
    for o, a in opts:
 
1911
        if o == "-t": mode = "l"        # list archive
 
1912
        if o == "-c": mode = "w"        # write to archive
 
1913
        if o == "-r": mode = "a"        # append to archive
 
1914
        if o == "-x": mode = "r"        # extract from archive
 
1915
        if o == "-f": file = a          # specify filename else use stdout
 
1916
        if o == "-C": dir  = a          # change to dir
 
1917
        if o == "-z": comp = "gz"       # filter through gzip
 
1918
        if o == "-j": comp = "bz2"      # filter through bzip2
 
1919
        if o == "-v": debug = 2         # verbose mode
 
1920
        if o == "-q": debug = 0         # quiet mode
 
1921
        if o == "--posix": posix = True # create posix compatible archive
 
1922
        if o == "-h":                   # help message
 
1923
            print usage
 
1924
            sys.exit(0)
 
1925
 
 
1926
    if mode is None:
 
1927
        print usage
 
1928
        sys.exit(0)
 
1929
 
 
1930
    mode = "%s:%s" % (mode, comp)
 
1931
 
 
1932
    if not file or file == "-":
 
1933
        if mode[0] != "w":
 
1934
            print usage
 
1935
            sys.exit(0)
 
1936
        debug = 0
 
1937
        # If under Win32, set stdout to binary.
 
1938
        try:
 
1939
            import msvcrt
 
1940
            msvcrt.setmode(1, os.O_BINARY)
 
1941
        except ImportError:
 
1942
            pass
 
1943
        tarfile = open("sys.stdout.tar", "%s:%s" % (mode, comp), sys.stdout)
 
1944
    else:
 
1945
        if mode[0] == "l":
 
1946
            tarfile = open(file, "r" + mode[1:])
 
1947
        else:
 
1948
            tarfile = open(file, mode)
 
1949
 
 
1950
    tarfile.debug = debug
 
1951
    tarfile.posix = posix
 
1952
 
 
1953
    if mode[0] == "r":
 
1954
        if dir is None:
 
1955
            dir = ""
 
1956
        for tarinfo in tarfile:
 
1957
            tarfile.extract(tarinfo, dir)
 
1958
    elif mode[0] == "l":
 
1959
        tarfile.list(debug)
 
1960
    else:
 
1961
        for arg in args:
 
1962
            files = glob.glob(arg)
 
1963
            for f in files:
 
1964
                try:
 
1965
                    tarfile.add(f, dir)
 
1966
                except Exception, e:
 
1967
                    print "ERROR:", e
 
1968
    tarfile.close()