From eaf3aab67795a6b34928d3e957e6762c92adc211 Mon Sep 17 00:00:00 2001 From: Bastian Kleineidam Date: Mon, 19 Nov 2012 22:27:54 +0100 Subject: [PATCH] Support archmage CHM files. --- doc/changelog.txt | 4 +++- patoolib/__init__.py | 12 ++++++++---- patoolib/programs/archmage.py | 32 ++++++++++++++++++++++++++++++++ patoolib/util.py | 2 ++ tests/archives/test_archmage.py | 32 ++++++++++++++++++++++++++++++++ tests/data/t.chm | Bin 0 -> 13506 bytes tests/data/t.chm.foo | Bin 0 -> 13506 bytes tests/test_mime.py | 3 +++ 8 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 patoolib/programs/archmage.py create mode 100644 tests/archives/test_archmage.py create mode 100644 tests/data/t.chm create mode 100644 tests/data/t.chm.foo diff --git a/doc/changelog.txt b/doc/changelog.txt index 579e0cf..7643e9e 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,7 +1,9 @@ 0.18 "" (released xx.xx.xxxx) * Fixed unadf archive listing. -* Added support for Python 3.x and depend on Python >= 2.7. +* Added support for Python 3.x. +* Use importlib to find program modules and depend on Python >= 2.7. +* Added support for archmage handling CHM (.chm) archives. 0.17 "I am Bruce Lee" (released 4.8.2012) diff --git a/patoolib/__init__.py b/patoolib/__init__.py index 1ba29f4..92ffd3c 100644 --- a/patoolib/__init__.py +++ b/patoolib/__init__.py @@ -29,9 +29,8 @@ ArchiveCommands = ('list', 'extract', 'test', 'create') # Supported archive formats ArchiveFormats = ( '7z', 'ace', 'adf', 'alzip', 'ape', 'ar', 'arc', 'arj', - 'bzip2', 'cab', 'compress', 'cpio', 'deb', 'dms', - 'flac', 'gzip', - 'lrzip', 'lzh', 'lzip', 'lzma', 'lzop', + 'bzip2', 'cab', 'chm', 'compress', 'cpio', 'deb', 'dms', + 'flac', 'gzip', 'lrzip', 'lzh', 'lzip', 'lzma', 'lzop', 'rar', 'rpm', 'rzip', 'shar', 'shn', 'tar', 'xz', 'zip', 'zoo') @@ -55,6 +54,7 @@ ArchiveMimetypes = { 'application/x-cab': 'cab', 'application/vnd.ms-cab-compressed': 'cab', 'application/x-arj': 'arj', + 'application/x-chm': 'chm', 'application/x-cpio': 'cpio', 'application/x-redhat-package-manager': 'rpm', 'application/x-rpm': 'rpm', @@ -125,6 +125,10 @@ ArchivePrograms = { 'list': ('cabextract', '7z'), 'test': ('cabextract', '7z'), }, + 'chm': { + 'extract': ('archmage',), + 'test': ('archmage',), + }, 'flac': { 'extract': ('flac',), 'test': ('flac',), @@ -629,5 +633,5 @@ def diff (archive1, archive2, verbose=False): def repack (archive1, archive2, verbose=False): - """Repacke archive to different file and/or format.""" + """Repack archive to different file and/or format.""" return handle_archive(archive1, 'repack', archive2, verbose=verbose) diff --git a/patoolib/programs/archmage.py b/patoolib/programs/archmage.py new file mode 100644 index 0000000..04fec1f --- /dev/null +++ b/patoolib/programs/archmage.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2012 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Archive commands for the archmage program.""" +import os +from .. import util + + +def extract_chm (archive, compression, cmd, **kwargs): + """Extract a CHM archive.""" + # archmage can only extract in non-existing directories + # so a nice dirname is created + name = util.get_single_outfile("", archive) + outdir = os.path.join(kwargs['outdir'], name) + return [cmd, '-x', os.path.abspath(archive), outdir] + + +def test_chm (archive, compression, cmd, **kwargs): + """Test a CHM archive.""" + return [cmd, '-d', os.path.abspath(archive)] diff --git a/patoolib/util.py b/patoolib/util.py index 66269c6..1d07a56 100644 --- a/patoolib/util.py +++ b/patoolib/util.py @@ -70,6 +70,7 @@ def add_mimedb_data(mimedb): add_mimetype(mimedb, 'audio/x-ape', '.ape') add_mimetype(mimedb, 'audio/x-shn', '.shn') add_mimetype(mimedb, 'audio/flac', '.flac') + add_mimetype(mimedb, 'application/x-chm', '.chm') def add_mimetype(mimedb, mimetype, extension): @@ -274,6 +275,7 @@ FileText2Mime = { "DMS archive data": "application/x-dms", "Monkey's Audio": "audio/x-ape", "FLAC audio bitstream data": "audio/flac", + "MS Windows HtmlHelp Data": "application/x-chm", } def guess_mime_file_text (file_prog, filename): diff --git a/tests/archives/test_archmage.py b/tests/archives/test_archmage.py new file mode 100644 index 0000000..2674aa7 --- /dev/null +++ b/tests/archives/test_archmage.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2012 Bastian Kleineidam +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +from . import ArchiveTest +from .. import needs_program + +class TestArchmage (ArchiveTest): + + program = 'archmage' + + @needs_program(program) + def test_archmage (self): + self.archive_extract('t.chm', check=None) + self.archive_test('t.chm') + + @needs_program('file') + @needs_program(program) + def test_archmage_file (self): + self.archive_extract('t.chm.foo', check=None) + self.archive_test('t.chm.foo') diff --git a/tests/data/t.chm b/tests/data/t.chm new file mode 100644 index 0000000000000000000000000000000000000000..9a328d8584a0289939d048e5435d7969b05a1302 GIT binary patch literal 13506 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 Sf6meO)0_)(yNH{s)&G6 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 Sf6m