########################################################################
# #
# This software is part of the ast package #
# Copyright (c) 1989-2011 AT&T Intellectual Property #
# and is licensed under the #
# Eclipse Public License, Version 1.0 #
# by AT&T Intellectual Property #
# #
# A copy of the License is available at #
# http://www.eclipse.org/org/documents/epl-v10.html #
# (with md5 checksum b35adb5213ca9657e911e9befb180842) #
# #
# Information and Software Systems Research #
# AT&T Research #
# Florham Park NJ #
# #
# Glenn Fowler <gsf@research.att.com> #
# #
########################################################################
# Make a shell archive
function logmsg
{
print -ru2 $command: "$@"
}
function err_exit
{
logmsg "$@"
exit 1
}
trap 'rm -rf /tmp/shar$$*' EXIT
trap 'err_exit qutting early' HUP INT QUIT TERM
case $(getopts '[-][123:xyz]' opt --xyz 2>/dev/null; echo 0$opt) in
0123) usage=$'
[-?
@(#)$Id: shar (AT&T Labs Research) 1999-04-20 $
]
'$USAGE_LICENSE$'
[+NAME? shar - create a shell archive]
[+DESCRIPTION?\bshar\b reads one or more input files and creates a
shell script which when executed will restore the contents
of these files. This is called a shell archive or \ashar\a.
The resulting archive is sent to standard output unless the
\b-o\b option is specified.]
[a:net-headers?Automatic generation of headers. The \b-n\b option
must also be specified. If the archive name does not
contain a \b/\b, then \b/part\b will be append to the
given archive name when constructing the header.]
[b:bits-per-code]:[bits?When doing compression, use \b-b \b\abits\a
as a parameter to \bcompress\b(1). The \b-b\b option turns on
\b-Z\b.]
[c:cut-mark?Start the shar with a cut line.]
[d:here-delimiter]:[string?Use \astring\a to delimit the files
in the shar instead of \bSHAR_EOF\b.]
[f:basenames?Use the basenames for the files.]
[g:level-for-gzip]:[level?When doing compression, use \b-\b\alevel\a
as a parameter to \bgzip\b(1). The \b-g\b option turns on
\b-z\b.]
[l:whole-size-limit]#[size?Limit the output file size to \asize\a
bytes. A suffix of \bb\b, \bk\b, or \bm\b can be specified
to indicate 512-byte blocks, kilobytes, or megabytes
respectively.]
[n:archive-name:name]:[name?Override automatically determined name
for the archive with \aname\a.]
[o:output-prefix]:[prefix?Save the archive files \aprefix\a\b.01\b
through \aprefix\a\b.\b\ann\a instead of standard output. This
option must be used when \b-l\b or the \b-L\b is specified.]
[p:intermix-type?Allow positional parameter options. The options
\b-B\b, \b-T\b, \b-z\b and \b-Z\b may be embedded, and files
to the right of the option will be processed in the specified
mode.]
[q:quit|silent?Do not output verbose messages locally when producing
the archive.]
[s:submitter]:[user?Override automatically determined submitter name
with \auser\a which is of the form \awho\a\b@\b\awhere\a.]
[t:tty?Write errors and the name of each file to \b/dev/tty\b as it is
archived.]
[w:no-character-count?Do NOT check each file with \bwc -c\b after
unpack.]
[z:gzip?\bgzip\b(1) and \buuencode\b(1) all files prior to packing.]
[B:uuencode,binary-files-files?Treat all files as binary and
\buuencode\b(1) prior to packing.]
[D:no-md5-digest?Do NOT use \bcksum md5sum\b digest to verify
the unpacked files. The default is to check.]
[L:split-size-limit]#[size?Limit the output file size to \asize\a
bytes as with \b-l\b, but split the archive into multiple
files.]
[Q:quiet-unshar?Verbose OFF. Disables the inclusion of comments to
be output when the archive is unpacked.]
[M:mixed-uuencode?Mixed mode. Determine if the files are text or
binary and archive correctly. Files found to be binary
are uuencoded prior to packing. This is the default.]
[S:stdin-file-list?Read list of files to be packed from the standard
input rather than from the command line in the format
generated by \bfind\b(1) and \btw\b(1). If \b-p\b is specified,
the options \b-B\b, \b-T\b, \b-z\b and \b-Z\b must be
included in the standard input.]
[T:text-files?Treat all files as text.]
[X:query-user?When unpacking, ask the user if files should be
overwritten.]
[Z:compress?\bcompress\b(1) and \buuencode\b(1) all files prior to
packing.]
[files ...]
[+SEE ALSO?\bcksum\b(1), \bcompress\b(1), \bfind\b(1), \bgzip\b(1),
\bpax\b(1), \btw\b(1), \buuencode\b(1), \bwc\b(1)]
'
;;
*)
usage='ab:[bits]cd:[delim]fg:[level]l#[size]n:[name]o:[prefix]pqs:[who]tzBDL#[size]MQSXTZ files ...'
;;
esac
IFS=$'\n'
command=${0##*/}
user=${USER:-${LOGNAME:-$(whoami 2>/dev/null || who am i | sed $'s/[ \t].*//')}}@$(hostname)
integer size=0
flags= prefix= mode=M arg=
separator=SHAR_EOF
# some strings that should be internationalized some day
skipping=$"echo 'x -' SKIPPING"
exists=$"'(file already exists)'"
extract=$"echo 'x -' extracting"
mkdir=$"creating directory"
uncomp=$"uncompressing file"
while getopts "$usage" c
do
case $c in
[acfpqwDSQX])
flags=$flags$c;;
b) mode=Z;arg=-"b $OPTARG";;
g) mode=z;arg=-$OPTARG;;
l) size=OPTARG;;
L) split=1 size=OPTARG;;
d) separator=$OPTARG;;
n) aname=$OPTARG;;
o) prefix=$OPTARG
exec > /tmp/shar$$.1;;
s) user=$OPTARG;;
q) verbose=;;
t) verbose=1; exec 2> /dev/tty;;
v) verbose=1;;
[zZBMT])
mode=$c;;
esac
done
# Check remaining arguments, which should be just a list of files:
shift $((OPTIND-1))
if (( $# ==0 )) && [[ $flags != *S* ]]
then err_exit "no arguments left!"
fi
contents='' # no files so far.
contdirs='' # no directories so far.
function preprocess_files
{
typeset file=$1
if [[ $flags == *p* && $file == -[zBTZ] ]]
then mode=${file#-}
elif [[ -f $file ]]
then if [[ $flags == *f* ]]
then contents="$contents$IFS${file##*/}"
else contents="$contents$IFS$file"
fi
elif [[ -d $file ]]
then if [[ $flags == *f* ]]
then err_exit "cannot archive directory $file with -b option."
fi
contdirs="$contdirs$IFS$file/ "
else err_exit "cannot archive $file"
fi
}
if [[ $flags == *S* ]]
then while read -r file
do preprocess_files "$file"
print -r -- "$file"
done > /tmp/shar$$.3
else for file
do preprocess_files "$file"
done
fi
if [[ $flags == *a* ]]
then if [[ $aname ]]
then if [[ $aname != */* ]]
then aname=$aname/part
fi
cat <<- !!!
Submitted-by: $user
Archive-name: ${aname}01
!!!
else err_exit "Cannot use -a without -n"
fi
fi
# Generate the prologue
# (The leading newline is for those who type csh instead of sh.)
if [[ $flags == *c* ]]
then print -r -- '#---- Cut Here and feed the following to sh ----'
fi
cat <<!!!
# This is a shell archive (produced by a ksh93 script).
# Remove anything before this line, then unpack it by saving
# it in a file and typing "sh file".
#
# Wrapped by on $(date) by $user
# Source directory was \`$(pwd)'.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
!!!
ls -df "#%8(size)d %(mode)s %(name)s" $contdirs $contents
print '\n#\nnocheck=$1'
if [[ $flags == *Q* ]]
then print quiet=:
else print quiet=false
fi
if [[ $flags == *x* ]]
then print nocheck=-c
fi
if [[ $flags != *D* ]]
then cat <<- \!!!
if uncompress < /dev/null > /dev/null 2>&1
then uncompress='uncompress -f'
elif gunzip < /dev/null > /dev/null 2>&1
then uncompress='gunzip -f'
fi
if md5sum /dev/null > /dev/null 2>&1
then md5=md5sum
elif cksum -x md5 /dev/null > /dev/null 2>&1
then md5='cksum -x md5'
fi
if touch -am 123456789 shar$$.3 > /dev/null 2>&1 && test -f shar$$.3
then touch=touch
else touch=:
echo 'WARNING: not restoring timestamps.'
fi
rm -f 123456789 shar$$.3
!!!
fi
if [[ $flags == *X* ]]
then cat <<- \!!!
#
# The unshar will be interactively queried
#
if test ! -t 2
then exec < /dev/tty
fi
if test "`echo -n | wc -c`" -gt 0
then nflag= cflag='\c'
else nflag=-n cflag=
fi
!!!
fi
function isbinary #file
{
[[ $(file -m /dev/null -M "$1") == @(*/ebcdic*|*/octet-stream|*system*/*) ]]
}
function emit_guard # file type
{
cat <<- !!!
echo \$nflag '? -' overwrite "$name" '[no, yes, all, quit] (no)?' \$cflag
read check
case \$check in
[Aa]*) nocheck=-c;;
[Qq]*) echo 'extraction aborted';exit 1;;
[Yy]*) ;;
*) skip=1;;
esac
fi
if test "\$skip" = 1
then
!!!
}
function makedirs # file
{
typeset file=$1
file=$(dirname "$file")
if [[ $file == @(.|/|//) ]]
then return
fi
makedirs "$file"
cat <<- !!!
if test ! -d '$file'
then test X"\$quiet" != X && echo 'x -' '$mkdir' '$file'
mkdir '$file'
fi
!!!
}
# Emit the files and their separators:
function pack_file
{
typeset file=$1
filetype=text binary=
if [[ -d $file ]]
then filteype=directory
else case $mode in
B) filetype=binary;;
z) filetype=gzipped;;
Z) filetype=compressed;;
M) if isbinary "$file"
then filetype=binary
binary=1
fi
esac
fi
# Decide which name to archive under.
if [[ $flags == *f* ]]
then name=${file##*/}
[[ $flags != *q* ]] && logmsg "a - $name [from $file] ($filetype)"
else name="$file"
[[ $flags != *q* ]] && logmsg "a - $file ($filetype)"
fi
print -r "# ============= $file =============="
if [[ $file == */* ]]
then makedirs "$file"
fi
# Emit either a mkdir or a cat/sed to extract the file.
if [[ -d $file ]]
then print "mkdir $file"
[[ flags != *Q* ]] && print "echo mkdir -- $file"
else filetype=text binary=
print 'skip='
printf "if\ttest -f %q && test \"X\$nocheck\" != X-c\nthen\t" "$file"
if [[ $flags == *X* ]]
then emit_guard
fi
printf "\$quiet || $skipping %q $exists\nelse \$quiet || $extract %q '(%s)'\n" "$file" "$file" "$filetype"
if [[ $mode == [BZz] || $binary ]]
then if [[ $mode == Z ]]
then compress $arg < $file > /tmp/shar$$.2
cname=$name.Z
dname=/tmp/shar$$.2
elif [[ $mode == z ]]
then gzip $arg < $file > /tmp/shar$$.2
dname=/tmp/shar$$.2
cname=$name.gz
else dname=$file
cname=$name
fi
print -r 'uudecode << \!!!!'
uuencode "$cname" < $dname
print "!!!!"
if [[ $mode == Z && flags != *Q* ]]
then printf "echo '$uncomp' %s\n\$uncompress %q\n" "$file" "$cname"
elif [[ $mode == z && flags != *Q* ]]
then printf "echo '$uncomp' %s\ngunzip %q\n" "$file" "$cname"
fi
else print -r "sed 's/^@//' > \"$name\" <<'$separator'"
sed -e 's/^[.~@]/@&/' -e 's/^From/@&/' "$file"
print -r $separator
fi
fi
# Emit chmod to set permissions on the extracted file;
# this keels over if the filename contains "?".
fmode=$(ls -df "%(mode)s" "$file")
fmode=${fmode/?@(???)@(???)@(???)/u=\1,g=\2,o=\3}
mtime=$(date -m -f %# "$file")
printf "chmod ${fmode//-} %q&&\n\t\$touch -am $mtime %q\n" "$name" "$name"
print "fi"
}
if [[ $flags == *S* ]]
then while read -r file
do pack_file "$file"
done < /tmp/shar$$.3
else for file
do pack_file "$file"
done
fi
# If the -c option was given, emit the checking epilogue:
# If the receiving machine has cksum, it will be used instead of wc
files=$(printf "%q " $contents)
if [[ $flags != *w* && $flags != *D* ]]
then cat <<- __END__
\$quiet || echo 'Inspecting for damage in transit...'
temp=/tmp/shar\$\$; dtemp=/tmp/.shar\$\$
trap "rm -f \$temp \$dtemp; exit" EXIT HUP INT QUIT TERM
if [ X"\$md5" != X ]
then cat > \$temp <<\!!!
$(
if [[ $flags == *b* ]]
then cksum -x md5 "$@" | sed 's=[^ ]*/=='
else cksum -x md5 $contents | sed 's=[^ ]*/=='
fi
)
!!!
cksum='cksum -x md5'
else cat > \$temp <<\!!!
$(
IFS=$'\n' # this line should not be needed
if [[ $flags == *b* ]]
then wc "$@" | sed 's=[^ ]*/=='
else wc $contents | sed 's=[^ ]*/=='
fi
)
!!!
cksum=wc
fi
\$cksum $files | sed 's=[^ ]*/==' | diff -b \$temp - >\$dtemp
if [ -s \$dtemp ]
then echo "Ouch [diff of \$cksum output]:" ; cat \$dtemp
else \$quiet || echo "No problems found."
fi
__END__
fi
# split or limit size
if [[ $prefix ]]
then if [[ $split ]]
then split -b $size -f "$prefix" /tmp/shar$$.1
elif (( $size && $(wc -c < /tmp/shar$$.1) > $size ))
then head -c $size /tmp/shar$$.1 > $prefix.01
else cp /tmp/shar$$.1 "$prefix.01"
fi
fi
print 'exit 0' # exit even if more input