[net.sources] Connoisseur's Shar: correction

ajs@hpfcla.UUCP (ajs) (01/03/85)

[My apologies; the version I posted last week was not the very latest, and
it's too late to call back now.  Anyway, here is what you really wanted all
along, probably.  Please respond to the person listed below, not to me.]

Here's the latest "Connoisseur's shar" and documentation.  It has a few
advantages over the previously-posted version:

1.  You can archive a whole directory subtree via:  shar `find dir -print`
2.  The original file's permissions/modes are duplicated upon unpacking.
3.  The EOF marker is guaranteed to be unique from what's in the archive files.
4.  A timestamp and personstamp is recorded in the archive.
5.  Lines beginning with characters that mailers don't like (tildes, dots,
	ampersands) are no longer dangerous.
6.  The table-of-contents line no longer overflows your mailer's maximum line.

These features also make this a candidate for being dubbed "The Glutton's Shar"
since it takes longer to start up.  The way to fix that would be to rewrite it
in C, but that seems to take away some of its novelty.  Still, a C version may
be forthcoming (especially if someone volunteers!)....

Kudos to Dan Hoey <hoey@NRL-AIC.ARPA>, who contributed immensely to shar's
current feature set, especially the directory-recursing code.  A tip of the
Hatlo hat also to Alan Silverstein, who set shar's clear coding style, fixed
bugs in earlier versions, and posted this to the Usenet since I don't have
write-access to it.

Bob Desinger
ucbvax!hpda!bd		hpda!bd@BERKELEY	ihnp4!hpfcla!hpda!bd
Hewlett-Packard Co.	11000 Wolfe Road	Cupertino, CA  95014


# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by hpdsb!bd on Wed Jan  2 09:40:22 PST 1985
# Contents:  shar shar.l
 
echo x - shar
sed 's/^@//' > "shar" <<'@//E*O*F shar//'

# UNISRC_ID: @(#)shar.sh	27.1	84/12/17  
: Make a shell archive package

# Usage: $0 [-b] [-c] [-t] [-v] files... > package
# See the manual entry for details.


# Initialize:

diagnostic='eval echo >&2'	# diagnostics to stderr by default.
trap '$diagnostic "$0: quitting early"; exit 1' 1 2 3 15
base_option=FALSE		# use pathnames, not basenames.
check_option=FALSE		# don't generate integrity check.
USAGE='Usage: $0 \[-b] \[-c] \[-t] \[-v] files... \> package'


# Extract and digest options, if any:
#
# Un-comment the "-)" line below to treat single dashes as a no-op.
# Commented means single dashes elicit a usage diagnostic.

while [ -n "$1" ]	# while there are more arguments,
do			# digest them; stop when you find a non-option.
	case "$1" in
	-b)	base_option=TRUE;	shift;;
	-c)	check_option=TRUE;	shift;;
	-v)	verbose=TRUE;		shift;;
	-t)	verbose=TRUE; diagnostic='eval echo >/dev/tty'; shift;;
###	-)	shift;;		# if uncommented, eat single dashes.
	-*)	$diagnostic $USAGE; exit 1;;	 # die at illegal options.
	*)	break;;		# non-option found.
	esac
done


# Check remaining arguments, which should be just a list of files:

if [ $# = 0 ]
then	# no arguments left!
	$diagnostic $USAGE
	exit 1
fi


# Check the cupboard to see if the ingredients are all there:

contents=''	# no files so far.
contdirs=''	# no directories so far.

for arg		# for all files specified,
do		# establish the archive name.
	if [ -f "$arg" ]
	then	# file exists and is not a directory.
		case $base_option in
		TRUE)	unpack_name=`basename "$arg"` ;;
		FALSE)	unpack_name="$arg" ;;
		esac

		contents="$contents $unpack_name"

	elif [ -d "$arg" ]
	then	# file exists and is a directory.
		case $base_option in
		TRUE)   $diagnostic '$0: cannot archive directory "$arg" with -b option.'
			exit 1 ;;
		FALSE)  contdirs="$contdirs $arg/ " ;;
		esac

	else	# not a plain file and not a directory.
		$diagnostic '$0: cannot archive "$arg"'
		exit 1
	fi
done


# Emit the prologue:
# (The leading newline is for those who type csh instead of sh.)

cat <<!!!

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by `who am i | sed 's/[ 	].*//'` on `date`
!!!


# Emit the list of ingredients:

# Simple version (breaks if you shar lots of files at once):
#	echo "# Contents: $contdirs$contents"
#
# Complex and cosmetic version to pack contents list onto lines that fit on
# one terminal line ("expr string : .*" prints the length of the string):

MAX=80
line='# Contents: '
for item in $contdirs $contents
do
	if [ `expr "$line" : '.*' + 1 + "$item" : '.*'` -lt $MAX ]
	then	# length of old line + new item is short enough,
		line="$line $item"	# so just append it.

	else	# new element makes line too long,
		echo "$line"		# so put it on a new line.
		line="#	$item"		# start a new line.
		MAX=74			# compensate for tab width.
	fi
done

echo "$line"
echo " "


# Emit the files and their separators:

for arg
do
	# Decide which name to archive under.
	case $base_option in
	TRUE)   unpack_name=`basename "$arg"`
		test $verbose && $diagnostic "a - $unpack_name [from $arg]" ;;
	FALSE)  unpack_name="$arg"
		test $verbose && $diagnostic "a - $arg" ;;
	esac

	# Emit either a mkdir or a cat/sed to extract the file.
	if [ -d "$arg" ]
	then
		echo "echo mkdir - $arg"
		echo "mkdir $arg"
	else
		echo "echo x - $unpack_name"
		separator="@//E*O*F $unpack_name//"
		echo "sed 's/^@//' > \"$unpack_name\" <<'$separator'"
		sed  -e 's/^[.~@]/@&/'  -e 's/^From/@&/'  "$arg"
		echo $separator
	fi

	# Emit chmod to set permissions on the extracted file;
	# this keels over if the filename contains "?".
	ls -ld $arg | sed \
		-e 's/^.\(...\)\(...\)\(...\).*/u=\1,g=\2,o=\3/' \
		-e 's/-//g' \
		-e 's?.*?chmod & '"$unpack_name?"
	echo " "
done


# If the -c option was given, emit the checking epilogue:
# (The sed script converts files to basenames so it works regardless of -b.)

if [ $check_option = TRUE ]
then
	echo 'echo Inspecting for damage in transit...'
	echo 'temp=/tmp/shar$$; dtemp=/tmp/.shar$$'
	echo 'trap "rm -f $temp $dtemp; exit" 0 1 2 3 15'
	echo 'cat > $temp <<\!!!'
	case $base_option in
	TRUE)   wc $@ | sed 's=[^ ]*/=='	;;
	FALSE)  wc $contents | sed 's=[^ ]*/=='	;;
	esac
	echo '!!!'
	echo "wc $contents | sed 's=[^ ]*/==' | "'diff -b $temp - >$dtemp'
	echo 'if [ -s $dtemp ]'
	echo 'then echo "Ouch [diff of wc output]:" ; cat $dtemp'
	echo 'else echo "No problems found."'
	echo 'fi'
fi


# Finish up:

echo 'exit 0'	# sharchives unpack even if junk follows.
exit 0
@//E*O*F shar//
chmod u=rwx,g=rx,o=rx shar
 
echo x - shar.l
sed 's/^@//' > "shar.l" <<'@//E*O*F shar.l//'
@.TH SHAR LOCAL HEWLETT-PACKARD
@.ad b
@.SH NAME
shar \- make a shell archive package
@.SH SYNOPSIS
\fBshar\fR [\fB-b\fR] [\fB-c\fR] [\fB-t\fR] [\fB-v\fR] file ...
@.SH DESCRIPTION
@.I Shar
bundles the named
@.IR file ( s )
into a single distribution package suitable for mailing or carrying around.
The files should be mailable (not object code, for instance).
@.IR Shar 's
resulting package, written to standard output, is an editable file.
It is actually a shell script using
@.IR sh (1)
"here" documents to extract its contents into the appropriate places.
@.PP
The package is unwrapped by running
@.IR sh
with the package name as an argument.
Its files are written to the pathnames recorded in the archive,
then permissions are set via
@.IR chmod (1)
to match the original files.
@.PP
Except with the
@.B \-b
option, a directory tree
@.I dir
can be archived using the command "shar `find
@.I dir
-print`".
@.PP
Available options are:
@.TP
@.B \-b
Archive files under their basenames, regardless of the original pathnames
specified.
The contents are thus unpacked into the current directory instead of to the
originally-specified pathnames.
This allows you to archive files from many directories but unpack them into a
single directory.
It also allows you to unpack, say,
@.I /etc/termcap
into
@.I ./termcap
instead of overwriting the original one in
@.IR /etc .
@.TP
@.B \-c
Append to the package a simple data-integrity check using
@.IR wc (1)
to insure that the contents were not damaged in transit.
This check will be performed automatically after unpacking.
@.TP
@.B \-t
Write diagnostics and messages directly to your terminal,
instead of to the standard error.  This is useful when invoking
@.I shar
from programs such as
@.IR vi (1)
which normally combine standard error with standard output.
Specifying
@.B \-t
also turns on the
@.B \-v
(verbose) option.
@.TP
@.B \-v
Announce archived file names as they are packed.
The
@.B \-t
option determines the destination for these announcements.
@.br
@.ne 5
@.SH FILES
/dev/tty               	if specified with \fB-t\fR
@.br
/tmp/shar*, /tmp/.shar*	when inspecting for damage
@.br
cat, echo, sed, chmod  	as subprocesses
@.br
basename, wc, mkdir    	as optional subprocesses
@.SH DIAGNOSTICS
@.I Shar
refuses to archive nonexistent files.
When the
@.B \-b
option is used, it refuses to archive directories.
@.I Shar
terminates and does no archiving if it encounters either problem.
@.PP
Exit status 1 is returned upon interrupt or trouble with arguments.
@.SH "SEE ALSO"
ar(1),
cpio(1),
find(1),
tar(1).
@.SH BUGS
Archived directories must appear before the files in them.  Failure to adhere
to this ordering is not detected, but the result will fail to unpack.
@.PP
Ownerships for archived files are not retained.
@.PP
The integrity check is very simple-minded.
In particular, it notices only if the number of characters, words, or lines
is altered; it fails to catch bits flipped during transmission.
@.PP
@.I Shar
should complain about binary files.
It should also complain about filenames with embedded spaces and
question marks, which
@.IR shar 's
subprocesses don't handle.
@.PP
There should be a standard way
to record the system on which the archive was created.
Berkeley hosts return this information via
@.IR "who am i" ,
but Bell-derived hosts often use wildly differing methods.
@//E*O*F shar.l//
chmod u=rw,g=rw,o=r shar.l
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
     176     771    4548 shar
     121     539    3292 shar.l
     297    1310    7840 total
!!!
wc  shar shar.l | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0