[go: up one dir, main page]

Menu

[r1450]: / mcomix / archive_tools.py  Maximize  Restore  History

Download this file

225 lines (188 with data), 6.6 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
"""archive_tools.py - Archive tool functions."""
import os
import re
import shutil
import zipfile
import tarfile
import tempfile
import operator
from mcomix import image_tools
from mcomix import constants
from mcomix import log
from mcomix.archive import (
lha_external,
pdf_external,
rar,
rar_external,
sevenzip_external,
tar,
zip,
zip_external,
)
# Handlers for each archive type.
_HANDLERS = {
constants.ZIP: (
zip.ZipArchive,
),
# Prefer 7z over zip executable for encryption and Unicode support.
constants.ZIP_EXTERNAL: (
sevenzip_external.SevenZipArchive,
zip_external.ZipArchive
),
constants.TAR: (
tar.TarArchive,
),
constants.GZIP: (
tar.TarArchive,
),
constants.BZIP2: (
tar.TarArchive,
),
constants.XZ: (
# No LZMA support in Python 2 tarfile module.
sevenzip_external.TarArchive,
),
constants.RAR: (
rar.RarArchive,
rar_external.RarArchive,
# Last resort: some versions of 7z support RAR.
sevenzip_external.SevenZipArchive,
),
# Prefer 7z over lha executable for Unicode support.
constants.LHA: (
sevenzip_external.SevenZipArchive,
lha_external.LhaArchive,
),
constants.SEVENZIP: (
sevenzip_external.SevenZipArchive,
),
constants.PDF: (
pdf_external.PdfArchive,
),
}
def _get_handler(archive_type):
""" Return best archive class for format <archive_type> """
for handler in _HANDLERS[archive_type]:
if not hasattr(handler, 'is_available'):
return handler
if handler.is_available():
return handler
def _is_available(archive_type):
""" Return True if a handler supporting the <archive_type> format is available """
return _get_handler(archive_type) is not None
def szip_available():
return _is_available(constants.SEVENZIP)
def rar_available():
return _is_available(constants.RAR)
def lha_available():
return _is_available(constants.LHA)
def pdf_available():
return _is_available(constants.PDF)
def get_supported_formats():
supported_formats = {}
for name, formats, is_available in (
('ZIP', constants.ZIP_FORMATS , True ),
('Tar', constants.TAR_FORMATS , True ),
('RAR', constants.RAR_FORMATS , rar_available() ),
('7z' , constants.SZIP_FORMATS, szip_available()),
('LHA', constants.LHA_FORMATS , lha_available() ),
('PDF', constants.PDF_FORMATS , pdf_available() ),
):
if is_available:
supported_formats[name] = formats
return supported_formats
# Set supported archive extensions regexp from list of supported formats.
SUPPORTED_ARCHIVE_REGEX = re.compile(r'\.(%s)$' %
'|'.join(sorted(reduce(
operator.add,
[map(re.escape, fmt[1]) for fmt
in get_supported_formats().values()]
))), re.I)
def is_archive_file(path):
"""Return True if the file at <path> is a supported archive file.
"""
return SUPPORTED_ARCHIVE_REGEX.search(path) is not None
def archive_mime_type(path):
"""Return the archive type of <path> or None for non-archives."""
try:
if os.path.isfile(path):
if not os.access(path, os.R_OK):
return None
if zipfile.is_zipfile(path):
if zip.is_py_supported_zipfile(path):
return constants.ZIP
else:
return constants.ZIP_EXTERNAL
fd = open(path, 'rb')
magic = fd.read(5)
fd.close()
try:
istarfile = tarfile.is_tarfile(path)
except IOError:
# Tarfile raises an error when accessing certain network shares
istarfile = False
if istarfile and os.path.getsize(path) > 0:
if magic.startswith('BZh'):
return constants.BZIP2
elif magic.startswith('\037\213'):
return constants.GZIP
else:
return constants.TAR
if magic[0:4] == 'Rar!':
return constants.RAR
if magic[0:4] == '7z\xBC\xAF':
return constants.SEVENZIP
# Headers for TAR-XZ and TAR-LZMA that aren't supported by tarfile
if magic[0:5] == '\xFD7zXZ' or magic[0:5] == ']\x00\x00\x80\x00':
return constants.XZ
if magic[2:4] == '-l':
return constants.LHA
if magic[0:4] == '%PDF':
return constants.PDF
except Exception:
log.warning(_('! Could not read %s'), path)
return None
def get_archive_info(path):
"""Return a tuple (mime, num_pages, size) with info about the archive
at <path>, or None if <path> doesn't point to a supported
"""
cleanup = []
try:
tmpdir = tempfile.mkdtemp(prefix=u'mcomix_archive_info.')
cleanup.append(lambda: shutil.rmtree(tmpdir, True))
mime = archive_mime_type(path)
archive = get_recursive_archive_handler(path, tmpdir, type=mime)
if archive is None:
return None
cleanup.append(archive.close)
files = archive.list_contents()
num_pages = len(filter(image_tools.SUPPORTED_IMAGE_REGEX.search, files))
size = os.stat(path).st_size
return (mime, num_pages, size)
finally:
for fn in reversed(cleanup):
fn()
def get_archive_handler(path, type=None):
""" Returns a fitting extractor handler for the archive passed
in <path> (with optional mime type <type>. Returns None if no matching
extractor was found.
"""
if type is None:
type = archive_mime_type(path)
if type is None:
return None
handler = _get_handler(type)
if handler is None:
return None
return handler(path)
def get_recursive_archive_handler(path, destination_dir, type=None):
""" Same as <get_archive_handler> but the handler will transparently handle
archives within archives.
"""
archive = get_archive_handler(path, type=type)
if archive is None:
return None
# XXX: Deferred import to avoid circular dependency
from mcomix.archive import archive_recursive
return archive_recursive.RecursiveArchive(archive, destination_dir)
# vim: expandtab:sw=4:ts=4