aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xTools/scripts/sunshar.rb299
-rw-r--r--Tools/scripts/sunshar/Makefile13
-rw-r--r--Tools/scripts/sunshar/sunshar.156
3 files changed, 368 insertions, 0 deletions
diff --git a/Tools/scripts/sunshar.rb b/Tools/scripts/sunshar.rb
new file mode 100755
index 000000000000..70b038e8958e
--- /dev/null
+++ b/Tools/scripts/sunshar.rb
@@ -0,0 +1,299 @@
+#!/usr/bin/env ruby
+# -*- ruby -*-
+#
+# Copyright (c) 2001-2004 Akinori MUSHA
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+RCS_ID = %q$Idaemons: /home/cvs/sunshar/sunshar.rb,v 1.13 2004/02/28 14:15:47 knu Exp $
+RCS_REVISION = RCS_ID.split[2]
+MYNAME = File.basename($0)
+
+require 'parsearg'
+require 'fileutils'
+require 'shellwords'
+require 'stringio'
+
+begin
+ require 'features/ruby18/dir'
+rescue LoadError; end
+
+$USAGE = 'usage'
+
+$strip_level = 0
+$force = false
+$dryrun = false
+$quiet = false
+$dir = nil
+
+def info(*s)
+ puts(*s) unless $quiet
+end
+
+def usage
+ print <<-EOF
+#{MYNAME} rev.#{RCS_REVISION}
+
+usage: #{MYNAME} [-fnq] [-p level] [file]
+ #{MYNAME} -h
+ -d dir chdir -- chdir to dir before extracting files
+ -f force -- allow overwriting, ignore errors
+ -h help -- show this help
+ -n dry run -- show what would have been extracted
+ -p N strip -- strip N levels from pathnames (cf. patch(1)\'s -p)
+ -q quiet -- be quiet
+ EOF
+end
+
+def main
+ parseArgs(0, nil, 'fhnq', 'd:', 'p:')
+
+ if $OPT_h
+ usage
+ exit 0
+ end
+
+ if $OPT_f
+ $force = true
+ end
+
+ if $OPT_n
+ $dryrun = true
+ end
+
+ if $OPT_q
+ $quiet = true
+ end
+
+ $dir = $OPT_d || '.'
+
+ if $OPT_p
+ $strip_level = $OPT_p.to_i rescue -1
+
+ if $strip_level < 0
+ STDERR.puts "negative value ignored: #{$OPT_p}"
+ end
+ end
+
+ nerrors = 0
+
+ if ARGV.empty?
+ info "extracting files from stdin into #{$dir}"
+
+ begin
+ Dir.chdir($dir) {
+ unshar_stream(STDIN)
+ }
+
+ info "done."
+ rescue => e
+ STDERR.puts "error in extracting stdin: #{e.message}"
+ nerrors += 1
+ end
+ else
+ for file in ARGV
+ info "extracting files from #{file} into #{$dir}"
+
+ begin
+ File.open(file) do |f|
+ Dir.chdir($dir) {
+ unshar_stream(f)
+ }
+ end
+
+ info "done."
+ rescue => e
+ STDERR.puts "error in extracting #{file}: #{e.message}"
+ nerrors += 1
+ end
+ end
+ end
+
+ exit nerrors
+end
+
+def unshar_stream(io)
+ e = nil
+
+ while line = io.gets
+ if /^(\s*)\# This is a shell archive/ =~ line
+ indent = $1.length
+ break
+ end
+ end
+
+ if io.eof?
+ raise "not a shell archive."
+ end
+
+ f = nil
+ prefix = nil
+ file = nil
+ boundary = nil
+
+ while line = io.gets
+ line.slice!(0, indent)
+
+ if f
+ if line.strip == boundary
+ f.close
+ f = nil
+ next
+ end
+
+ if line.sub!(/^#{Regexp.quote(prefix)}/, '')
+ f.print line
+ else
+ raise "line #{io.lineno}: broken archive: #{line}"
+ end
+
+ next
+ end
+
+ case line
+ when /^exit\s*$/
+ break
+ when /^echo\s+(.+)$/
+ # info $1
+ when /^mkdir\s+(?:-p\s+)?(.+)$/
+ dir = nil
+
+ Shellwords.shellwords($1).each do |word|
+ if /^[^\-]/ =~ word
+ dir = word
+ break
+ end
+ end
+
+ next if dir.nil?
+
+ dir = strip_filename(dir.strip + '/')
+ if dir.chomp('/').empty?
+ next
+ end
+
+ begin
+ FileUtils.mkdir_p(dir) unless $dryrun
+ info "c - #{dir}"
+ rescue => e
+ info "c - #{dir} ... failed: #{e.message}"
+ raise e
+ end
+ when /sed\s+(.+)>(.+)<<(.+)/
+ prefix = Shellwords.shellwords($1).first
+ file = Shellwords.shellwords($2).first
+ boundary = Shellwords.shellwords($3).first
+
+ next unless prefix && file && boundary
+
+ if /s(.)\^(.*)\1\1/ =~ prefix
+ prefix = $2
+ else
+ next
+ end
+
+ file = strip_filename(file)
+
+ next if file.empty? || boundary.empty?
+
+ overwrite = false
+
+ if File.exist?(file)
+ if $force
+ overwrite = true
+ else
+ info "x - #{file} ... skipped"
+ next
+ end
+ end
+
+ dir = File.dirname(file)
+
+ if !File.directory?(dir + "/.")
+ begin
+ FileUtils.mkdir_p(dir) unless $dryrun
+ info "d - #{dir}"
+ rescue => e
+ info "d - #{dir} ... failed: #{e.message}"
+ raise e
+ end
+ end
+
+ begin
+ f = $dryrun ? StringIO.new : File.open(file, 'w')
+ if overwrite
+ info "x - #{file} ... overwritten"
+ else
+ info "x - #{file}"
+ end
+ rescue => e
+ info "x - #{file} ... failed! (#{e.message})"
+
+ if $force
+ f = nil
+ next
+ else
+ raise e
+ end
+ end
+ end
+ end
+
+ raise e if e
+end
+
+def strip_filename(file)
+ sfile = file.gsub(%r"/{2,}", "/")
+
+ if 0 < $strip_level
+ sfile.sub!(%r"^([^/]*/){1,#{$strip_level}}", '')
+ end
+
+ case sfile
+ when %r"^[~/]"
+ raise "reference to absolute directory: #{file} (use -p N)"
+ when %r"(^|/)\.\.(?:/|$)"
+ raise "reference to parent directory: #{file} (use -p N)"
+ end
+
+ sfile
+end
+
+def signal_handler(sig)
+ info "\nInterrupted."
+
+ exit 255
+end
+
+if $0 == __FILE__
+ for sig in [2, 3, 15]
+ trap(sig) do
+ signal_handler(sig)
+ end
+ end
+
+ main
+end
diff --git a/Tools/scripts/sunshar/Makefile b/Tools/scripts/sunshar/Makefile
new file mode 100644
index 000000000000..581e717591f5
--- /dev/null
+++ b/Tools/scripts/sunshar/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+# $Idaemons: /home/cvs/sunshar/Makefile,v 1.1.1.1 2001/09/09 13:49:08 knu Exp $
+
+PREFIX?= /usr/local
+BINDIR= ${PREFIX}/bin
+MANDIR= ${PREFIX}/man/man
+
+SCRIPTS= sunshar.rb
+MAN= sunshar.1
+
+.PATH: ${.CURDIR}/..
+
+.include <bsd.prog.mk>
diff --git a/Tools/scripts/sunshar/sunshar.1 b/Tools/scripts/sunshar/sunshar.1
new file mode 100644
index 000000000000..ee24f229fbc5
--- /dev/null
+++ b/Tools/scripts/sunshar/sunshar.1
@@ -0,0 +1,56 @@
+.\" $FreeBSD$
+.\" $Idaemons: /home/cvs/sunshar/sunshar.1,v 1.2 2004/02/28 14:14:53 knu Exp $
+.\"
+.Dd September 9, 2001
+.Dt SUNSHAR 1
+.Os FreeBSD
+.Sh NAME
+.Nm sunshar
+.Nd a secure unshar
+.Sh SYNOPSIS
+.Nm
+.Op Fl hfnq
+.Op Fl p Ar number
+.Op Ar file ...
+.Sh DESCRIPTION
+The
+.Nm
+command extracts files from the given shell archive(s). If no file
+name is given, it reads from the standard input.
+.Pp
+It brings you security because it never executes dangerous commands
+possibly contained in a shell archive. Also, it does not overwrite
+existing files unless the
+.Fl f
+option is specified.
+.Sh OPTIONS
+The following command line arguments are supported:
+.Pp
+.Bl -tag -width "-p number" -compact
+.It Fl h
+Show help and exit.
+.Pp
+.It Fl f
+Allow overwriting existing files, and ignore errors and continue.
+.Pp
+.It Fl p Ar number
+Strip
+.Ar number
+levels of path components from path names. (cf.
+.Xr patch 1 's
+.Fl p
+option)
+.Pp
+.It Fl n
+Do not extract anything but just show what would have been extracted.
+.Pp
+.It Fl q
+Be quiet.
+.El
+.Sh SEE ALSO
+.Xr shar 1
+.Sh AUTHORS
+.An Akinori MUSHA Aq knu@iDaemons.org
+.Sh BUGS
+.Nm
+only supports shell archives made with BSD shar.