scripts/dtrx

branch
trunk
changeset 22
b240777ae53e
parent 20
69c93c3e6972
child 23
039dd321a7d0
equal deleted inserted replaced
21:fd3e10410040 22:b240777ae53e
43 WITHOUT ANY WARRANTY; without even the implied warranty of 43 WITHOUT ANY WARRANTY; without even the implied warranty of
44 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 44 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
45 Public License for more details.""" % (VERSION,) 45 Public License for more details.""" % (VERSION,)
46 46
47 MATCHING_DIRECTORY = 1 47 MATCHING_DIRECTORY = 1
48 # ONE_DIRECTORY = 2 48 ONE_ENTRY = 2
49 BOMB = 3 49 BOMB = 3
50 EMPTY = 4 50 EMPTY = 4
51 COMPRESSED = 5 51 ONE_ENTRY_KNOWN = 5
52 52
53 EXTRACT_HERE = 1 53 EXTRACT_HERE = 1
54 EXTRACT_WRAP = 2 54 EXTRACT_WRAP = 2
55 EXTRACT_RENAME = 3 55 EXTRACT_RENAME = 3
56 56
169 self.archive.flush() 169 self.archive.flush()
170 170
171 def prepare(self): 171 def prepare(self):
172 pass 172 pass
173 173
174 def check_contents(self): 174 def check_included_archive(self, filename):
175 archive_type = None 175 if extractor_map.has_key(mimetypes.guess_type(filename)[0]):
176 filenames = self.get_filenames() 176 self.included_archives.append(filename)
177 try: 177
178 filename = filenames.next() 178 def check_first_filename(self, filenames):
179 if extractor_map.has_key(mimetypes.guess_type(filename)[0]): 179 try:
180 self.included_archives.append(filename) 180 first_filename = filenames.next()
181 first_part = filename.split('/', 1)[0] + '/'
182 except StopIteration: 181 except StopIteration:
183 filenames.stop() 182 filenames.stop()
184 return EMPTY 183 return (None, None)
184 self.check_included_archive(first_filename)
185 parts = first_filename.split('/')
186 first_part = [parts[0]]
187 if parts[0] == '.':
188 first_part.append(parts[1])
189 return (first_filename, '/'.join(first_part + ['']))
190
191 def check_second_filename(self, filenames, first_part, first_filename):
192 try:
193 filename = filenames.next()
194 except StopIteration:
195 return ONE_ENTRY, first_filename
196 self.check_included_archive(filename)
197 if not filename.startswith(first_part):
198 return BOMB, None
199 return None, first_part
200
201 def check_contents(self):
202 filenames = self.get_filenames()
203 first_filename, first_part = self.check_first_filename(filenames)
204 if first_filename is None:
205 return (EMPTY, None)
206 archive_type, type_info = self.check_second_filename(filenames,
207 first_part,
208 first_filename)
185 for filename in filenames: 209 for filename in filenames:
186 if extractor_map.has_key(mimetypes.guess_type(filename)[0]): 210 self.check_included_archive(filename)
187 self.included_archives.append(filename) 211 if (archive_type != BOMB) and (not filename.startswith(first_part)):
188 if (archive_type is None) and (not filename.startswith(first_part)):
189 archive_type = BOMB 212 archive_type = BOMB
213 type_info = None
190 filenames.stop() 214 filenames.stop()
191 if archive_type: 215 if archive_type is None:
192 return archive_type 216 if self.basename() == first_part[:-1]:
193 if self.basename() == first_part[:-1]: 217 archive_type = MATCHING_DIRECTORY
194 return MATCHING_DIRECTORY 218 else:
195 return first_part 219 archive_type = ONE_ENTRY
220 return archive_type, type_info
196 221
197 def basename(self): 222 def basename(self):
198 pieces = os.path.basename(self.filename).split('.') 223 pieces = os.path.basename(self.filename).split('.')
199 extension = '.' + pieces[-1] 224 extension = '.' + pieces[-1]
200 if mimetypes.encodings_map.has_key(extension): 225 if mimetypes.encodings_map.has_key(extension):
268 pieces.pop() 293 pieces.pop()
269 return '.'.join(pieces) 294 return '.'.join(pieces)
270 295
271 def check_contents(self): 296 def check_contents(self):
272 CpioExtractor.check_contents(self) 297 CpioExtractor.check_contents(self)
273 return BOMB 298 return (BOMB, None)
274 299
275 300
276 class DebExtractor(TarExtractor): 301 class DebExtractor(TarExtractor):
277 def prepare(self): 302 def prepare(self):
278 self.pipe(['ar', 'p', self.filename, 'data.tar.gz'], 303 self.pipe(['ar', 'p', self.filename, 'data.tar.gz'],
289 return BaseExtractor.basename(self) 314 return BaseExtractor.basename(self)
290 return '_'.join(pieces) 315 return '_'.join(pieces)
291 316
292 def check_contents(self): 317 def check_contents(self):
293 TarExtractor.check_contents(self) 318 TarExtractor.check_contents(self)
294 return BOMB 319 return (BOMB, None)
295 320
296 321
297 class CompressionExtractor(BaseExtractor): 322 class CompressionExtractor(BaseExtractor):
298 name_checker = FilenameChecker 323 name_checker = FilenameChecker
299 324
306 331
307 def get_filenames(self): 332 def get_filenames(self):
308 yield self.basename() 333 yield self.basename()
309 334
310 def check_contents(self): 335 def check_contents(self):
311 return COMPRESSED 336 return (ONE_ENTRY_KNOWN, self.basename())
312 337
313 def extract(self, path): 338 def extract(self, path):
314 output = open(path, 'w') 339 output = open(path, 'w')
340 self.archive.seek(0, 0)
315 self.run(['cat'], "output write", stdin=self.archive, stdout=output) 341 self.run(['cat'], "output write", stdin=self.archive, stdout=output)
316 output.close() 342 output.close()
317 343
318 344
319 class BaseHandler(object): 345 class BaseHandler(object):
320 def __init__(self, extractor, contents, options): 346 def __init__(self, extractor, contents, content_name, options):
321 self.logger = logging.getLogger('dtrx-log') 347 self.logger = logging.getLogger('dtrx-log')
322 self.extractor = extractor 348 self.extractor = extractor
323 self.contents = contents 349 self.contents = contents
350 self.content_name = content_name
324 self.options = options 351 self.options = options
325 self.target = None 352 self.target = None
326 353
327 def extract(self): 354 def extract(self):
328 try: 355 try:
351 # Match . . tempdir + checked 378 # Match . . tempdir + checked
352 # Bomb . basename DirectoryChecked 379 # Bomb . basename DirectoryChecked
353 380
354 class FlatHandler(BaseHandler): 381 class FlatHandler(BaseHandler):
355 def can_handle(contents, options): 382 def can_handle(contents, options):
356 return ((options.flat and (contents != COMPRESSED)) or 383 return ((options.flat and (contents != ONE_ENTRY_KNOWN)) or
357 (options.overwrite and (contents == MATCHING_DIRECTORY))) 384 (options.overwrite and (contents == MATCHING_DIRECTORY)))
358 can_handle = staticmethod(can_handle) 385 can_handle = staticmethod(can_handle)
359 386
360 def __init__(self, extractor, contents, options): 387 def __init__(self, extractor, contents, content_name, options):
361 BaseHandler.__init__(self, extractor, contents, options) 388 BaseHandler.__init__(self, extractor, contents, content_name, options)
362 self.target = '.' 389 self.target = '.'
363 390
364 def cleanup(self): 391 def cleanup(self):
365 for filename in self.extractor.get_filenames(): 392 for filename in self.extractor.get_filenames():
366 stat_info = os.stat(filename) 393 stat_info = os.stat(filename)
370 os.chmod(filename, stat_info.st_mode | perms) 397 os.chmod(filename, stat_info.st_mode | perms)
371 398
372 399
373 class OverwriteHandler(BaseHandler): 400 class OverwriteHandler(BaseHandler):
374 def can_handle(contents, options): 401 def can_handle(contents, options):
375 return ((options.flat and (contents == COMPRESSED)) or 402 return ((options.flat and (contents == ONE_ENTRY_KNOWN)) or
376 (options.overwrite and (contents != MATCHING_DIRECTORY))) 403 (options.overwrite and (contents != MATCHING_DIRECTORY)))
377 can_handle = staticmethod(can_handle) 404 can_handle = staticmethod(can_handle)
378 405
379 def __init__(self, extractor, contents, options): 406 def __init__(self, extractor, contents, content_name, options):
380 BaseHandler.__init__(self, extractor, contents, options) 407 BaseHandler.__init__(self, extractor, contents, content_name, options)
381 self.target = self.extractor.basename() 408 self.target = self.extractor.basename()
382 409
383 410
384 class MatchHandler(BaseHandler): 411 class MatchHandler(BaseHandler):
385 def can_handle(contents, options): 412 def can_handle(contents, options):
386 return ((contents == MATCHING_DIRECTORY) or 413 return ((contents == MATCHING_DIRECTORY) or
387 (hasattr(contents, 'encode') and 414 ((contents == ONE_ENTRY) and
388 (options.onedir_policy in (EXTRACT_RENAME, EXTRACT_HERE)))) 415 (options.onedir_policy in (EXTRACT_RENAME, EXTRACT_HERE))))
389 can_handle = staticmethod(can_handle) 416 can_handle = staticmethod(can_handle)
390 417
391 def extract(self): 418 def extract(self):
392 if self.contents == MATCHING_DIRECTORY: 419 if self.contents == MATCHING_DIRECTORY:
393 basename = destination = self.extractor.basename() 420 basename = destination = self.extractor.basename()
394 elif self.options.onedir_policy == EXTRACT_HERE: 421 elif self.options.onedir_policy == EXTRACT_HERE:
395 basename = destination = self.contents.rstrip('/') 422 basename = destination = self.content_name.rstrip('/')
396 else: 423 else:
397 basename = self.contents.rstrip('/') 424 basename = self.content_name.rstrip('/')
398 destination = self.extractor.basename() 425 destination = self.extractor.basename()
399 self.target = tempdir = tempfile.mkdtemp(dir='.') 426 self.target = tempdir = tempfile.mkdtemp(dir='.')
400 result = BaseHandler.extract(self) 427 result = BaseHandler.extract(self)
401 if result is None: 428 if result is None:
402 checker = self.extractor.name_checker(destination) 429 checker = self.extractor.name_checker(destination)
409 class EmptyHandler(object): 436 class EmptyHandler(object):
410 def can_handle(contents, options): 437 def can_handle(contents, options):
411 return contents == EMPTY 438 return contents == EMPTY
412 can_handle = staticmethod(can_handle) 439 can_handle = staticmethod(can_handle)
413 440
414 def __init__(self, extractor, contents, options): pass 441 def __init__(self, extractor, contents, content_name, options): pass
415 def extract(self): pass 442 def extract(self): pass
416 def cleanup(self): pass 443 def cleanup(self): pass
417 444
418 445
419 class BombHandler(BaseHandler): 446 class BombHandler(BaseHandler):
420 def can_handle(contents, options): 447 def can_handle(contents, options):
421 return True 448 return True
422 can_handle = staticmethod(can_handle) 449 can_handle = staticmethod(can_handle)
423 450
424 def __init__(self, extractor, contents, options): 451 def __init__(self, extractor, contents, content_name, options):
425 BaseHandler.__init__(self, extractor, contents, options) 452 BaseHandler.__init__(self, extractor, contents, content_name, options)
426 checker = self.extractor.name_checker(self.extractor.basename()) 453 checker = self.extractor.name_checker(self.extractor.basename())
427 self.target = checker.check() 454 self.target = checker.check()
428 455
429 456
430 extractor_map = {'application/x-tar': TarExtractor, 457 extractor_map = {'application/x-tar': TarExtractor,
494 if self.options.batch: 521 if self.options.batch:
495 return answers[''] 522 return answers['']
496 last_line = question.pop() 523 last_line = question.pop()
497 while True: 524 while True:
498 print "\n".join(question) 525 print "\n".join(question)
499 answer = raw_input(last_line) 526 try:
527 answer = raw_input(last_line)
528 except EOFError:
529 return answers['']
500 try: 530 try:
501 return answers[answer.lower()] 531 return answers[answer.lower()]
502 except KeyError: 532 except KeyError:
503 print 533 print
504 534
517 except ExtractorError, error: 547 except ExtractorError, error:
518 return str(error) 548 return str(error)
519 549
520 def get_handler(self): 550 def get_handler(self):
521 try: 551 try:
522 content = self.current_extractor.check_contents() 552 content, content_name = self.current_extractor.check_contents()
523 if hasattr(content, 'encode'): # Archive contains one directory. 553 if content == ONE_ENTRY:
524 question = textwrap.wrap("%s contains one directory: %s. %s" % 554 question = textwrap.wrap("%s contains one entry: %s." %
525 (self.current_filename, content, 555 (self.current_filename, content_name))
526 "You can:"))
527 question.extend(["You can:", 556 question.extend(["You can:",
528 " * extract it Inside another directory", 557 " * extract it Inside another directory",
529 " * extract it and Rename the directory", 558 " * extract it and Rename the directory",
530 " * extract it Here", 559 " * extract it Here",
531 "What do you want to do? (I/r/h) "]) 560 "What do you want to do? (I/r/h) "])
532 self.options.onedir_policy = \ 561 self.options.onedir_policy = \
533 self.ask_question(question, self.policy_answers) 562 self.ask_question(question, self.policy_answers)
534 for handler in handlers: 563 for handler in handlers:
535 if handler.can_handle(content, self.options): 564 if handler.can_handle(content, self.options):
536 self.current_handler = handler(self.current_extractor, 565 self.current_handler = handler(self.current_extractor,
537 content, self.options) 566 content, content_name,
567 self.options)
538 break 568 break
539 except ExtractorError, error: 569 except ExtractorError, error:
540 return str(error) 570 return str(error)
541 571
542 def recurse(self): 572 def recurse(self):

mercurial