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)