diff --git a/doc/changelog.txt b/doc/changelog.txt index 1508614..3d873b0 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -1,11 +1,12 @@ 0.17 "" (released xx.xx.2012) -* Added support for Monkey's Audio Compressor handling APE (.ape) files. -* Added support for shorten handling SHN (.shn) files. -* Added support for flac handling FLAC (.flac) files. +* Added support for Monkey's Audio Compressor handling APE (.ape) archives. +* Added support for shorten handling SHN (.shn) archives. +* Added support for flac handling FLAC (.flac) archives. * Added support for the BSD cpio and tar programs. -* Added support for lhasa handling LZH (.lzh, .lha) files. -* Added support for lcap handling CAB (.cab) files. +* Added support for lhasa handling LZH (.lzh, .lha) archives. +* Added support for lcap handling CAB (.cab) archives. +* Added support for shar handling SHAR (.shar) shell archives. * Generate standalone Windows .exe and Linux .rpm installer. * Initialize the internal MIME database correct on all platforms. * Improved option compatibility for the ar, cpio and tar programs. diff --git a/patoolib/__init__.py b/patoolib/__init__.py index 4f2f65c..ad3d608 100644 --- a/patoolib/__init__.py +++ b/patoolib/__init__.py @@ -24,7 +24,7 @@ ArchiveCommands = ('list', 'extract', 'test', 'create') # Supported archive formats ArchiveFormats = ('7z', 'ace', 'alzip', 'ape', 'ar', 'arc', 'arj', 'bzip2', 'cab', 'compress', 'cpio', 'deb', 'dms', 'flac', 'gzip', 'lrzip', 'lzh', - 'lzip', 'lzma', 'lzop', 'rar', 'rpm', 'rzip', 'shn', 'tar', 'xz', + 'lzip', 'lzma', 'lzop', 'rar', 'rpm', 'rzip', 'shar', 'shn', 'tar', 'xz', 'zip', 'zoo') # Supported compressions (used with tar for example) @@ -64,6 +64,7 @@ ArchiveMimetypes = { 'application/x-rzip': 'rzip', 'application/x-zoo': 'zoo', 'application/x-dms': 'dms', + 'application/x-shar': 'shar', 'audio/x-ape': 'ape', 'audio/x-shn': 'shn', 'audio/flac': 'flac', @@ -127,7 +128,7 @@ ArchivePrograms = { 'create': ('zip',), }, 'gzip': { - None: ('pigz', 'gzip', '7z', '7za'), + None: ('7z', '7za', 'pigz', 'gzip'), 'extract': ('py_gzip',), 'create': ('py_gzip',), }, @@ -198,6 +199,10 @@ ArchivePrograms = { 'list': ('py_echo',), 'create': ('rzip',), }, + 'shar': { + 'create': ('shar',), + 'extract': ('unshar',), + }, 'shn': { 'extract': ('shorten',), 'list': ('py_echo',), diff --git a/patoolib/programs/shar.py b/patoolib/programs/shar.py new file mode 100644 index 0000000..d341404 --- /dev/null +++ b/patoolib/programs/shar.py @@ -0,0 +1,26 @@ +# -*- 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 shar program.""" +from patoolib import util + +def create_shar (archive, compression, cmd, *args, **kwargs): + """Create a SHAR archive.""" + cmdlist = [util.shell_quote(cmd)] + if not kwargs['verbose']: + cmdlist.append('-q') + cmdlist.extend([util.shell_quote(x) for x in args]) + cmdlist.extend(['>', util.shell_quote(archive)]) + return (cmdlist, {'shell': True}) diff --git a/patoolib/programs/unshar.py b/patoolib/programs/unshar.py new file mode 100644 index 0000000..7753fc9 --- /dev/null +++ b/patoolib/programs/unshar.py @@ -0,0 +1,22 @@ +# -*- 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 unshar program.""" +import os + +def extract_shar (archive, compression, cmd, **kwargs): + """Extract a SHAR archive.""" + cmdlist = [cmd, os.path.abspath(archive)] + return (cmdlist, {'cwd': kwargs['outdir']}) diff --git a/patoolib/util.py b/patoolib/util.py index 48e8db9..0338db5 100644 --- a/patoolib/util.py +++ b/patoolib/util.py @@ -64,6 +64,7 @@ def add_mimedb_data(mimedb): add_mimetype(mimedb, 'application/x-zoo', '.zoo') add_mimetype(mimedb, 'application/x-dms', '.dms') add_mimetype(mimedb, 'application/x-zip-compressed', '.crx') + add_mimetype(mimedb, 'application/x-shar', '.shar') add_mimetype(mimedb, 'audio/x-ape', '.ape') add_mimetype(mimedb, 'audio/x-shn', '.shn') add_mimetype(mimedb, 'audio/flac', '.flac') diff --git a/tests/__init__.py b/tests/__init__.py index 3c28feb..f36395f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -175,6 +175,8 @@ class ArchiveTest (unittest.TestCase): command = 'list' elif self.program == 'lcab': program = 'cabextract' + elif self.program == 'shar': + return patoolib._handle_archive(archive, command, program=program) def check_created_archive_with_diff(self, archive, srcfile): @@ -191,6 +193,8 @@ class ArchiveTest (unittest.TestCase): program = 'unzip' elif self.program == 'lcab': program = 'cabextract' + elif self.program == 'shar': + program = 'unshar' tmpdir = patoolib.util.tmpdir(dir=basedir) try: olddir = os.getcwd() diff --git a/tests/data/t.shar b/tests/data/t.shar new file mode 100644 index 0000000..fc88aba --- /dev/null +++ b/tests/data/t.shar @@ -0,0 +1,144 @@ +#!/bin/sh +# This is a shell archive (produced by GNU sharutils 4.11). +# To extract the files from this archive, save it to some FILE, remove +# everything before the `#!/bin/sh' line above, then type `sh FILE'. +# +lock_dir=_sh09682 +# +# Existing files will *not* be overwritten, unless `-c' is specified. +# +# This shar contains: +# length mode name +# ------ ---------- ------------------------------------------ +# 2 -rw-r--r-- t.txt +# +MD5SUM=${MD5SUM-md5sum} +f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'` +test -n "${f}" && md5check=true || md5check=false +${md5check} || \ + echo 'Note: not verifying md5sums. Consider installing GNU coreutils.' +if test "X$1" = "X-c" +then keep_file='' +else keep_file=true +fi +echo=echo +save_IFS="${IFS}" +IFS="${IFS}:" +gettext_dir= +locale_dir= +set_echo=false + +for dir in $PATH +do + if test -f $dir/gettext \ + && ($dir/gettext --version >/dev/null 2>&1) + then + case `$dir/gettext --version 2>&1 | sed 1q` in + *GNU*) gettext_dir=$dir + set_echo=true + break ;; + esac + fi +done + +if ${set_echo} +then + set_echo=false + for dir in $PATH + do + if test -f $dir/shar \ + && ($dir/shar --print-text-domain-dir >/dev/null 2>&1) + then + locale_dir=`$dir/shar --print-text-domain-dir` + set_echo=true + break + fi + done + + if ${set_echo} + then + TEXTDOMAINDIR=$locale_dir + export TEXTDOMAINDIR + TEXTDOMAIN=sharutils + export TEXTDOMAIN + echo="$gettext_dir/gettext -s" + fi +fi +IFS="$save_IFS" +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null +then if (echo -n test; echo 1,2,3) | grep n >/dev/null + then shar_n= shar_c=' +' + else shar_n=-n shar_c= ; fi +else shar_n= shar_c='\c' ; fi +f=shar-touch.$$ +st1=200112312359.59 +st2=123123592001.59 +st2tr=123123592001.5 # old SysV 14-char limit +st3=1231235901 + +if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \ + test ! -f ${st1} && test -f ${f}; then + shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"' + +elif touch -am ${st2} ${f} >/dev/null 2>&1 && \ + test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then + shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"' + +elif touch -am ${st3} ${f} >/dev/null 2>&1 && \ + test ! -f ${st3} && test -f ${f}; then + shar_touch='touch -am $3$4$5$6$2 "$8"' + +else + shar_touch=: + echo + ${echo} 'WARNING: not restoring timestamps. Consider getting and +installing GNU `touch'\'', distributed in GNU coreutils...' + echo +fi +rm -f ${st1} ${st2} ${st2tr} ${st3} ${f} +# +if test ! -d ${lock_dir} ; then : +else ${echo} "lock directory ${lock_dir} exists" + exit 1 +fi +if mkdir ${lock_dir} +then ${echo} "x - created lock directory ${lock_dir}." +else ${echo} "x - failed to create lock directory ${lock_dir}." + exit 1 +fi +# ============= t.txt ============== +if test -n "${keep_file}" && test -f 't.txt' +then +${echo} "x - SKIPPING t.txt (file already exists)" +else +${echo} "x - extracting t.txt (binary)" + sed 's/^X//' << 'SHAR_EOF' | uudecode && +begin 600 t.txt +"-#)B +` +end +SHAR_EOF + (set 20 12 05 25 20 11 37 't.txt' + eval "${shar_touch}") && \ + chmod 0644 't.txt' +if test $? -ne 0 +then ${echo} "restore of t.txt failed" +fi + if ${md5check} + then ( + ${MD5SUM} -c >/dev/null 2>&1 || ${echo} 't.txt': 'MD5 check failed' + ) << \SHAR_EOF +a1d0c6e83f027327d8461063f4ac58a6 t.txt +SHAR_EOF + else +test `LC_ALL=C wc -c < 't.txt'` -ne 2 && \ + ${echo} "restoration warning: size of 't.txt' is not 2" + fi +fi +if rm -fr ${lock_dir} +then ${echo} "x - removed lock directory ${lock_dir}." +else ${echo} "x - failed to remove lock directory ${lock_dir}." + exit 1 +fi +exit 0 diff --git a/tests/test_archives.py b/tests/test_archives.py index f7fd3ec..5c4e2eb 100644 --- a/tests/test_archives.py +++ b/tests/test_archives.py @@ -514,3 +514,13 @@ class TestArchives (ArchiveTest): self.archive_extract('t.flac', check=None) self.archive_test('t.flac') self.archive_create('t.flac', srcfile="t.wav") + + @needs_program('shar') + def test_shar (self): + self.program = 'shar' + self.archive_create('t.shar', singlefile=True) + + @needs_program('unshar') + def test_unshar (self): + self.program = 'unshar' + self.archive_extract('t.shar', check=None) diff --git a/tests/test_mime.py b/tests/test_mime.py index 0b8083e..77cdfdf 100644 --- a/tests/test_mime.py +++ b/tests/test_mime.py @@ -190,6 +190,7 @@ class TestMime (unittest.TestCase): self.mime_test_mimedb("t.rz", "application/x-rzip", None) self.mime_test_mimedb("t.zoo", "application/x-zoo", None) self.mime_test_mimedb("t.dms", "application/x-dms", None) + self.mime_test_mimedb("t.shar", "application/x-shar", None) self.mime_test_mimedb("t.ape", "audio/x-ape", None) self.mime_test_mimedb("t.shn", "audio/x-shn", None) self.mime_test_mimedb("t.flac", "audio/flac", None)