diff options
-rwxr-xr-x | Tools/scripts/sunshar.rb | 299 | ||||
-rw-r--r-- | Tools/scripts/sunshar/Makefile | 13 | ||||
-rw-r--r-- | Tools/scripts/sunshar/sunshar.1 | 56 |
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. |