######################################################################## # # # 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 # # # ######################################################################## # 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 < /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