[net.sources] Improved Connoisseur's Shar

ajs@hpfcla.UUCP (ajs) (12/28/84)

[Posted for someone else, by request, because mod.sources is not yet
 reaching many sites.  Please respond to address below, not to ajs.]

Here's the latest "Connoisseur's shar" and documentation.  Actually, it's an
upgrade to the Conn. Shar originally posted to net.sources a few months ago.
It runs on both Berkeley and Bell Unixes.  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 and fixed
bugs in earlier versions, and who posted this for me since I don't have write-
access to the Usenet.

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".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# shar shar.1

echo x - shar
cat > "shar" << '//E*O*F shar//'

# UNISRC_ID: @(#)shar.sh	26.2	84/09/06  
: 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
	-[bB])	base_option=TRUE;				shift ;;
	-[cC])	check_option=TRUE;				shift ;;
	-[tT])	verbose=TRUE; diagnostic='eval echo >/dev/tty';	shift ;;
	-[vV])	verbose=TRUE;					shift ;;
    ##	-)	shift ;;			# eat single dashes.
	-*)	$diagnostic $usage; exit 1 ;;	# die at illegal options.
	*)	break ;;
	esac
    done


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

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


# Emit the prologue, then check and list ingredients:
# (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".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
!!!

	contents=''				# no files so far.

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

		contents="$contents $unpack_name"

	    else
		$diagnostic "$0: cannot archive $arg"
		exit 1
	    fi
	done

	echo "#$contents"
	echo


# Emit the files and their separators:

	for arg		# for all arguments left (file names).
	do
	    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

	    separator="//E*O*F $unpack_name//"
	    echo "echo x - $unpack_name"
	    echo "cat > \"$unpack_name\" << '$separator'"
	    cat "$arg"
	    echo $separator
	    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 Possible errors detected by \'wc\' [hopefully none]:"
	    echo 'temp=/tmp/shar$$'
	    echo 'trap "rm -f $temp; exit" 0 1 2 3 15'	# will clean up.
	    echo 'cat > $temp <<\!!!'
	    wc $@ | sed 's=[^ ]*/=='
	    echo '!!!'
	    echo "wc $contents | sed 's=[^ ]*/==' | "'diff -b $temp -'
	fi


# Finish up:

	echo 'exit 0'		# sharchives unpack even if junk follows.
	exit 0
//E*O*F shar//

echo x - shar.1
cat > "shar.1" << '//E*O*F shar.1//'
.TH SHAR 1 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] \fIfiles...\fR
.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 themselves mailable, e.g. not object code.
.IR Shar 's
resulting package, written to standard output, is an editable ASCII file.
It is actually a shell script which uses \fIsh\fR\^(1) "here" documents to
extract its contents into appropriate places.
.PP
The package is unwrapped by running \fIsh\fR\^(1) with the package name as an
argument.
Its files are written to the pathnames recorded in the archive.
.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
Cause
.I shar
to append to the package a simple data-integrity check using
.I wc
to insure that the contents were not damaged in transit.
This check is performed automatically after unpacking.
.TP
.B \-t
Write diagnostics and messages directly to
.IR /dev/tty ,
your terminal, instead of to standard error.
This is useful when invoking
.I shar
from programs such as
.I vi
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 affects where these announcements go.
.SH FILES
/dev/tty    	if specified by the \fB-t\fR option
.br
/tmp/shar*  	used for the insurance check after unpacking
.br
cat, echo   	as subprocesses
.br
basename, wc	as optional subprocesses
.SH DIAGNOSTICS
.I Shar
refuses to archive nonexistent files and directories.
It terminates and does no archiving if it encounters them.
.PP
Exit status 1 is returned upon interrupt or trouble with arguments.
.SH "SEE ALSO"
ar(1),
basename(1),
cat(1),
cpio(1),
echo(1),
sh(1),
tar(1),
wc(1).
.SH BUGS
Ownerships and permissions for archived files are not retained.
.PP
The integrity check is very simple-minded.
In particular, it only notices if the number of characters, words, or lines is
altered.
It fails to catch bits flipped during transmission.
.PP
There should be a way to archive directories by name instead of using arguments
such as
.IR dir/* .
//E*O*F shar.1//

echo Possible errors detected by \'wc\' [hopefully none]:
temp=/tmp/$0$$
trap "rm -f $temp; exit" 0 1 2 3 15
cat > $temp <<\!!!
    123    480   3083 shar
     99    425   2644 shar.1
    222    905   5727 total
!!!
wc  shar shar.1 | sed 's=[^ ]*/==' | diff -b $temp -
exit 0

perlman@wanginst.UUCP (Gary Perlman) (01/04/85)

> Here's the latest "Connoisseur's shar" and documentation.
> 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!)

Sigh!  I recently posted the 2nd version of my C version of shar.
More people sent me suggestions, and these led to this new version.
Just about every option of every shar has been added.
It also works, which is a feature by net.sources standards.
This new version, because it has many options, uses getopt,
compliments of Henry Spencer of U Toronto.
The new options allow:
	1.  checking the size of the files as they are unpacked (-c)
	2.  adding a line prefix to avoid problems with special characters (-p)
	3.  changing the default end of file delimiter (-d)
	4.  extract to the base name of named files (-b)
	5.  request all the options (-a)
Old options allowed:
	1.  verbose output while extracting (-v)
	2.  copying directory structures

It is interesting that this program has been helped along
by many people working through the net.
I think it would be more useful if people send suggestions
to me instead of posting tangential versions themselves.
As you can see in the manual entry, credits are given.

Gary Perlman/Wang Institute/Tyngsboro, MA/01879/(617) 649-9731

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
-----cut here-----cut here-----cut here-----cut here-----
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	shar.1
#	shar.c
#	traverse.3
#	traverse.c
#	getopt.3
#	getopt.c
#	makefile
# This archive created: Thu Jan  3 16:05:02 1985
# By:	Gary Perlman (Wang Institute, Tyngsboro, MA 01879 USA)
echo shar: extracting shar.1 '(1873 characters)'
cat << \SHAR_EOF > shar.1
.TH SHAR 1net "December 26, 1984"
.SH NAME
shar \- create file storage archive for extraction by /bin/sh
.SH SYNOPSIS
.B shar
[-d delim] [-p prefix] [-bcv] files
.SH DESCRIPTION
.I shar
prints its input files with special lines around them
to be used by the shell,
.I /bin/sh ,
to extract them later.
The output can be filtered through the shell to
recreate copies of the original files.
.SS Options
.TP
.B -a
All the options.
The options:
.ce
-v -c -b -p XX
are implied.
.TP
.B -v
Print verbose feedback messages about what
.I shar
is doing to be printed during extraction.
Sizes of plain files are echoed to allow a simple validity check.
.TP
.B -c
Check file size on extraction by counting characters.
An error message is reported to the person doing the
extraction if the sizes don't match.
.TP
.B -b
Extract files into basenames so that files with absolute path names
are put into the current directory.
This option has no effect when directories are archived.
.TP
.B -d delim
Use this as the ``end of file'' delimiter instead of the default.
The only reason to change it is if you suspect an file
contains the default delimiter:
.B SHAR_EOF
.TP
.B -p prefix
Use this as the prefix to each line of the archived files.
This is to make sure that special characters are not
eaten up by programs like mailers.
If this option is used,
the files will be extracted with the stream editor
.B sed
rather than
.B cat
so it is more efficient and portable to avoid setting the prefix.
.PP
.I shar
allows directories to be named, and
.I shar
prints the necessary commands to create new directories and fill them.
.SH AUTHOR
Gary Perlman
(based on a shell version by James Gosling)
(many additions motivated by Derek Zahn & Michael Thompson)
.SH BUGS
.I shar
does not know anything about:
links between files,
binary files,
overwriting existing files,
or
protection modes of files.
SHAR_EOF
if test 1873 -ne "`wc -c shar.1`"
then
echo shar: error transmitting shar.1 '(should have been 1873 characters)'
fi
echo shar: extracting shar.c '(5074 characters)'
cat << \SHAR_EOF > shar.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

/*{
Shar puts readable text files together in a package
from which they are easy to extract.  The original version
was a shell script posted to the net, shown below:
	#Date: Mon Oct 18 11:08:34 1982
	#From: decvax!microsof!uw-beave!jim (James Gosling at CMU)
	AR=$1
	shift
	for i do
		echo a - $i
		echo "echo x - $i" >>$AR
		echo "cat >$i <<'!Funky!Stuff!'" >>$AR
		cat $i >>$AR
		echo "!Funky!Stuff!" >>$AR
	done
I rewrote this version in C to provide better diagnostics
and to run faster.  The major difference is that my version
does not affect any files because it prints to the standard
output.  Mine also has several options.

Gary Perlman/Wang Institute/Tyngsboro, MA/01879/(617) 649-9731

Many enhancements motivated by Michael Thompson.

Directory archiving motivated by Derek Zahn @ wisconsin
	His version had some problems, so I wrote a general
	routine for traversing a directory hierarchy.  It
	allows marching through a directory on old and new
	UNIX systems.

}*/

/* OPTIONS */
int 	Verbose = 0;           /* provide append/extract feedback */
int 	Basename = 0;          /* extract into basenames */
int 	Count = 0;             /* count characters to check transfer */
char	*Delim = "SHAR_EOF";   /* put after each file */
char	Filter[100] = "cat";   /* used to extract archived files */
char	*Prefix = NULL;        /* line prefix to avoid funny chars */
#define	SED "sed 's/^%s//'"    /* used to remove prefix from lines */

#define	USAGE "[-a] [-p prefix] [-d delim] [-bcv] files > archive"
#define	OPTSTRING "ap:d:bcv"

main (argc, argv) char **argv;	
	{
	extern	int 	optind;
	extern	char	*optarg;
	int 	errflg = 0;
	int 	shar ();
	int 	C;
	while ((C = getopt (argc, argv, OPTSTRING)) != EOF)
		switch (C)
			{
			case 'v': Verbose++; break;
			case 'c': Count++; break;
			case 'b': Basename++; break;
			case 'd': Delim = optarg; break;
			case 'a': /* all the options */
				optarg = "XX";
				Verbose++;
				Count++;
				Basename++;
				/* fall through to set prefix */
			case 'p': (void) sprintf (Filter, SED, Prefix = optarg); break;
			default: errflg++;
			}
	if (errflg || optind == argc)
		{
		if (optind == argc)
			fprintf (stderr, "shar: No input files\n");
		fprintf (stderr, "USAGE: shar %s\n", USAGE);
		exit (1);
		}
	if (header (argc, argv, optind))
		exit (2);
	while (optind < argc)
		traverse (argv[optind++], shar);
	footer ();
	exit (0);
	}

header (argc, argv, optind)
char	**argv;
	{
	int 	i;
	int 	problems = 0;
	long	clock;
	char	*ctime ();
	char	*getenv ();
	char	*NAME = getenv ("NAME");
	char	*ORG = getenv ("ORGANIZATION");
	for (i = optind; i < argc; i++)
		if (access (argv[i], 4)) /* check read permission */
			{
			fprintf (stderr, "shar: Can't read '%s'\n", argv[i]);
			problems++;
			}
	if (problems) return (problems);
	puts ("#\tThis is a shell archive.");
	puts ("#\tRemove everything above and including the cut line.");
	puts ("#\tThen run the rest of the file through sh.");
	puts ("-----cut here-----cut here-----cut here-----cut here-----");
	puts ("#!/bin/sh");
	puts ("# shar:	Shell Archiver");
	puts ("#\tRun the following text with /bin/sh to create:");
	for (i = optind; i < argc; i++)
		printf ("#\t%s\n", argv[i]);
	(void) time (&clock);
	printf ("# This archive created: %s", ctime (&clock));
	if (NAME)
		printf ("# By:\t%s (%s)\n", NAME, ORG ? ORG : "");
	return (0);
	}

footer ()
	{
	puts ("#\tEnd of shell archive");
	puts ("exit 0");
	}

archive (input, output)
char	*input, *output;
	{
	char	line[BUFSIZ];
	FILE	*ioptr;
	if (ioptr = fopen (input, "r"))
		{
		printf ("%s << \\%s > %s\n", Filter, Delim, output);
		while (fgets (line, BUFSIZ, ioptr))
			{
			if (Prefix) fputs (Prefix, stdout);
			fputs (line, stdout);
			}
		puts (Delim);
		(void) fclose (ioptr);
		return (0);
		}
	else
		{
		fprintf (stderr, "shar: Can't open '%s'\n", input);
		return (1);
		}
	}

#define	FSIZE statbuf.st_size
shar (file, type, pos)
char	*file;
	{
	struct	stat	statbuf;
	char	*basefile = file;
	if (!strcmp (file, ".")) return;
	if (stat (file, &statbuf)) FSIZE = 0;
	if (Basename)
		{
		while (*basefile) basefile++; /* go to end of name */
		while (basefile > file && *(basefile-1) != '/') basefile--;
		}
	if (pos == 0)
		{
		if (type == 'd')
			{
			if (Verbose)
				printf ("echo shar: creating directory %s\n", file);
			printf ("mkdir %s\n", file);
			printf ("chdir %s\n", file);
			}
		else /* type == 'f' */
			{
			if (Verbose)
				printf ("echo shar: extracting %s '(%d characters)'\n",
					basefile, FSIZE);
			if (archive (file, basefile)) exit (-1);
			}
		}
	else /* pos == 1 */
		{
		if (type == 'd')
			{
			if (Verbose)
				printf ("echo shar: done with directory %s\n", file);
			printf ("chdir ..\n");
			}
		else /* type == 'f' (plain file) */
			{
			if (Count)
				{
				printf ("if test %d -ne \"`wc -c %s`\"\n",
					FSIZE, basefile);
				puts ("then");
				printf ("echo shar: error transmitting %s ", basefile);
				printf ("'(should have been %d characters)'\n", FSIZE);
				puts ("fi");
				}
			}
		}
	}
SHAR_EOF
if test 5074 -ne "`wc -c shar.c`"
then
echo shar: error transmitting shar.c '(should have been 5074 characters)'
fi
echo shar: extracting traverse.3 '(1050 characters)'
cat << \SHAR_EOF > traverse.3
.TH TRAVERSE 3WI "December 16, 1984"
.SH NAME
traverse \- recursively traverse a directory
.SH SYNOPSIS
.nf
traverse (path, func)
char	*path;
int 	(*func) ();

func (path, filetype, position)
char	*path;
.fi
.SH DESCRIPTION
traverse
applies its argument function func to its argument file pathname path.
If path is a directory,
then traverse applies func to all its entries.
.PP
The argument func should take three parameters:
a file name,
a file type,
and a position.
The call looks like this for directories:
.ce
(*func) (path, 'd', position);
and like this for other files:
.ce
(*func) (path, 'f', position);
The position
is 0 when path is first encountered
and 1 when traverse is done.
This is used to allow processing before and after
a directory is processed.
.SH EXAMPLE
.nf
list (name, type, pos)
char	*name;
	{
	if (type == 'd')
		printf ("%s %s\en", pos ? "Leaving" : "Entering", name);
	else /* type == 'f' */
		printf ("	%s\en", name);
	}
.fi
.SH AUTHOR
Gary Perlman
.SH BUGS
There are no diagnostics when directories cannot be searched.
SHAR_EOF
if test 1050 -ne "`wc -c traverse.3`"
then
echo shar: error transmitting traverse.3 '(should have been 1050 characters)'
fi
echo shar: extracting traverse.c '(1769 characters)'
cat << \SHAR_EOF > traverse.c
/*LINTLIBRARY*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>

#ifdef MAXNAMLEN

#define	namedir(entry) (entry->d_name)
#define	MAXNAME 256

#else

#define	DIR	FILE
#define	MAXNAME (DIRSIZ+2)
#define	opendir(path) fopen (path, "r")
#define closedir(dirp) fclose (dirp)
struct direct *
readdir (dirp)
DIR 	*dirp;
	{
	static	struct	direct	entry;
	if (dirp == NULL) return (NULL);
	for (;;)
		{
		if (fread (&entry, sizeof (struct direct), 1, dirp) == 0) return (NULL);
		if (entry.d_ino) return (&entry);
		}
	}
char	*strncpy ();
char *
namedir (entry)
struct	direct	*entry;
	{
	static	char	name[MAXNAME];
	return (strncpy (name, entry->d_name, DIRSIZ));
	}

#endif

#include <sys/stat.h>
#define	isdir(path) (stat(path, &buf) ? 0 : (buf.st_mode&S_IFMT)==S_IFDIR)

traverse (path, func)
char	*path;
int 	(*func) ();
	{
	DIR 	*dirp;
	struct	direct	*entry;
	struct	stat	buf;
	int 	filetype = isdir (path) ? 'd' : 'f';
	(*func) (path, filetype, 0);
	if (filetype == 'd')
		{
		if (chdir (path) == 0)
			{
			if (dirp = opendir ("."))
				{
				while (entry = readdir (dirp))
					{
					char	name[MAXNAME];
					(void) strcpy (name, namedir (entry));
					if (strcmp(name, ".") && strcmp(name, ".."))
						traverse (name, func);
					}
				(void) closedir(dirp);
				}
			(void) chdir ("..");
			}
		}
	(*func) (path, filetype, 1);
	}

#ifdef STANDALONE

static	Indent = 0;
tryverse (file, type, pos)
char	*file;
	{
	int 	in;
	if (pos == 0)
		{
		for (in = 0; in < Indent; in++) putchar ('\t');
		if (type == 'd')
			{
			printf ("%s/\n", file);
			Indent++;
			}
		else puts (file);
		}
	else if (type == 'd') Indent--;
	}

main (argc, argv) char **argv;
	{
	int 	tryverse ();
	char	*root = argc > 1 ? argv[1] : ".";
	traverse (root, tryverse);
	}
#endif
SHAR_EOF
if test 1769 -ne "`wc -c traverse.c`"
then
echo shar: error transmitting traverse.c '(should have been 1769 characters)'
fi
echo shar: extracting getopt.3 '(2755 characters)'
cat << \SHAR_EOF > getopt.3
.TH GETOPT 3 local
.DA 25 March 1982
.SH NAME
getopt \- get option letter from argv
.SH SYNOPSIS
.ft B
int getopt(argc, argv, optstring)
.br
int argc;
.br
char **argv;
.br
char *optstring;
.sp
extern char *optarg;
.br
extern int optind;
.ft
.SH DESCRIPTION
.I Getopt
returns the next option letter in
.I argv
that matches a letter in
.IR optstring .
.I Optstring
is a string of recognized option letters;
if a letter is followed by a colon, the option is expected to have
an argument that may or may not be separated from it by white space.
.I Optarg
is set to point to the start of the option argument on return from
.IR getopt .
.PP
.I Getopt
places in
.I optind
the
.I argv
index of the next argument to be processed.
Because
.I optind
is external, it is normally initialized to zero automatically
before the first call to 
.IR getopt .
.PP
When all options have been processed (i.e., up to the first
non-option argument),
.I getopt
returns
.BR EOF .
The special option
.B \-\-
may be used to delimit the end of the options;
.B EOF
will be returned, and
.B \-\-
will be skipped.
.SH SEE ALSO
getopt(1)
.SH DIAGNOSTICS
.I Getopt
prints an error message on
.I stderr
and returns a question mark
.RB ( ? )
when it encounters an option letter not included in
.IR optstring .
.SH EXAMPLE
The following code fragment shows how one might process the arguments
for a command that can take the mutually exclusive options
.B a
and
.BR b ,
and the options
.B f
and
.BR o ,
both of which require arguments:
.PP
.RS
.nf
main(argc, argv)
int argc;
char **argv;
{
	int c;
	extern int optind;
	extern char *optarg;
	\&.
	\&.
	\&.
	while ((c = getopt(argc, argv, "abf:o:")) != EOF)
		switch (c) {
		case 'a':
			if (bflg)
				errflg++;
			else
				aflg++;
			break;
		case 'b':
			if (aflg)
				errflg++;
			else
				bproc();
			break;
		case 'f':
			ifile = optarg;
			break;
		case 'o':
			ofile = optarg;
			break;
		case '?':
		default:
			errflg++;
			break;
		}
	if (errflg) {
		fprintf(stderr, "Usage: ...");
		exit(2);
	}
	for (; optind < argc; optind++) {
		\&.
		\&.
		\&.
	}
	\&.
	\&.
	\&.
}
.RE
.PP
A template similar to this can be found in
.IR /usr/pub/template.c .
.SH HISTORY
Written by Henry Spencer, working from a Bell Labs manual page.
Behavior believed identical to the Bell version.
.SH BUGS
It is not obvious how
`\-'
standing alone should be treated;  this version treats it as
a non-option argument, which is not always right.
.PP
Option arguments are allowed to begin with `\-';
this is reasonable but reduces the amount of error checking possible.
.PP
.I Getopt
is quite flexible but the obvious price must be paid:  there is much
it could do that it doesn't, like
checking mutually exclusive options, checking type of
option arguments, etc.
SHAR_EOF
if test 2755 -ne "`wc -c getopt.3`"
then
echo shar: error transmitting getopt.3 '(should have been 2755 characters)'
fi
echo shar: extracting getopt.c '(1437 characters)'
cat << \SHAR_EOF > getopt.c
/* got this off net.sources */
#include <stdio.h>

/*
 * get option letter from argument vector
 */
int	opterr = 1,		/* useless, never set or used */
	optind = 1,		/* index into parent argv vector */
	optopt;			/* character checked for validity */
char	*optarg;		/* argument associated with option */

#define BADCH	(int)'?'
#define EMSG	""
#define tell(s)	fputs(*nargv,stderr);fputs(s,stderr); \
		fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);

getopt(nargc,nargv,ostr)
int	nargc;
char	**nargv,
	*ostr;
{
	static char	*place = EMSG;	/* option letter processing */
	register char	*oli;		/* option letter list index */
	char	*index();

	if(!*place) {			/* update scanning pointer */
		if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF);
		if (*place == '-') {	/* found "--" */
			++optind;
			return(EOF);
		}
	}				/* option letter okay? */
	if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) {
		if(!*place) ++optind;
		tell(": illegal option -- ");
	}
	if (*++oli != ':') {		/* don't need argument */
		optarg = NULL;
		if (!*place) ++optind;
	}
	else {				/* need an argument */
		if (*place) optarg = place;	/* no white space */
		else if (nargc <= ++optind) {	/* no arg */
			place = EMSG;
			tell(": option requires an argument -- ");
		}
	 	else optarg = nargv[optind];	/* white space */
		place = EMSG;
		++optind;
	}
	return(optopt);			/* dump back option letter */
}
SHAR_EOF
if test 1437 -ne "`wc -c getopt.c`"
then
echo shar: error transmitting getopt.c '(should have been 1437 characters)'
fi
echo shar: extracting makefile '(161 characters)'
cat << \SHAR_EOF > makefile
CFLAGS=-O
shar: shar.o traverse.o getopt.o
	cc -o shar shar.o traverse.o getopt.o
archive: shar
	shar -cv shar.[c1] traverse.[c3] getopt.[c3] makefile > shar.sh
SHAR_EOF
if test 161 -ne "`wc -c makefile`"
then
echo shar: error transmitting makefile '(should have been 161 characters)'
fi
#	End of shell archive
exit 0