[comp.sources.wanted] Looking for shar

takahash@bnrmtv.UUCP (Alan Takahashi) (03/09/88)

I'm looking around for something that creates 'shar' files.  I
think it's called 'shar' (of all things!).

Not having ftp access, could someone EMAIL it to me?

Thanx.
 
------------------------------------------------------------------------------
Alan Takahashi                            !     hplabs   amdahl
Bell-Northern Research                    !          \   / 
Mountain View, CA                         !   .....!{-----}!bnrmtv!takahashi
                                          !          /   \
"When you need to knock on wood is when   !    3comvax   ames
  you realize the world's composed of     !-----------------------------------
  aluminum and vinyl." -- Flugg's Law     ! DISCLAIMER: It's all an illusion.
------------------------------------------------------------------------------

hobbs@bnrmtv.UUCP (Greg Hobbs) (03/24/89)

I am looking for a copy of shar or a shar-like program.
Please reply by e-mail.

Thank you,

Greg Hobbs

-- 
Voice:  (415) 940-2061 
UUCP:   ...{hplabs | amdahl | ames}!bnrmtv!hobbs
ARPANET:bnrmtv!hobbs@ames.arpa
mail:   BNR Inc., 685A E. Middlefield Rd., Mountain View, CA 94039

bickel@nprdc.arpa (Steven Bickel) (03/25/89)

In article <5140@bnrmtv.UUCP> hobbs@bnrmtv.UUCP (Greg Hobbs) writes:
>I am looking for a copy of shar or a shar-like program.
>Please reply by e-mail.
>

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	shar
# This archive created: Fri Mar 24 11:58:50 1989
export PATH; PATH=/bin:/usr/bin:$PATH
if test ! -d 'shar'
then
	mkdir 'shar'
fi
cd 'shar'
if test -f 'MANIFEST'
then
	echo shar: "will not over-write existing file 'MANIFEST'"
else
cat << \SHAR_EOF > 'MANIFEST'
   File Name		Archive #	Description
-----------------------------------------------------------
 MANIFEST                  1	This shipping list
 getopt.3                  1	
 getopt.c                  1	
 makefile                  1	
 shar.1                    1	
 shar.c                    1	
 traverse.3                1	
 traverse.c                1	
SHAR_EOF
fi
if test -f 'getopt.3'
then
	echo shar: "will not over-write existing file 'getopt.3'"
else
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
.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
fi
if test -f 'getopt.c'
then
	echo shar: "will not over-write existing file 'getopt.c'"
else
cat << \SHAR_EOF > 'getopt.c'
/*
	I got this off net.sources from Henry Spencer.
	It is a public domain getopt(3) like in System V.
	I have made the following modifications:

	index(s,c) was added because too many people could
	not compile getopt without it.

	A test main program was added, ifdeffed by GETOPT.
	This main program is a public domain implementation
	of the getopt(1) program like in System V.  The getopt
	program can be used to standardize shell option handling.
		e.g.  cc -DGETOPT getopt.c -o getopt
*/
#include <stdio.h>

#ifndef lint
static	char	sccsfid[] = "@(#) getopt.c 5.0 (UTZoo) 1985";
#endif

#define	ARGCH    (int)':'
#define BADCH	 (int)'?'
#define EMSG	 ""
#define	ENDARGS  "--"

/* this is included because index is not on some UNIX systems */
static
char *
index (s, c)
register	char	*s;
register	int 	c;
	{
	while (*s)
		if (c == *s) return (s);
		else s++;
	return (NULL);
	}

/*
 * 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 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++) == ARGCH || !(oli = index(ostr,optopt))) {
		if(!*place) ++optind;
		tell(": illegal option -- ");
	}
	if (*++oli != ARGCH) {		/* 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 */
}


#ifdef GETOPT

#ifndef lint
static	char	sccspid[] = "@(#) getopt.c 5.1 (WangInst) 6/15/85";
#endif

main (argc, argv) char **argv;
	{
	char	*optstring = argv[1];
	char	*argv0 = argv[0];
	extern	int 	optind;
	extern	char	*optarg;
	int 	opterr = 0;
	int 	C;
	char	*opi;
	if (argc == 1)
		{
		fprintf (stderr, "Usage: %s optstring args\n", argv0);
		exit (1);
		}
	argv++;
	argc--;
	argv[0] = argv0;
	while ((C = getopt (argc, argv, optstring)) != EOF)
		{
		if (C == BADCH) opterr++;
		printf ("-%c ", C);
		opi = index (optstring, C);
		if (opi && opi[1] == ARGCH)
			if (optarg)
				printf ("\"%s\" ", optarg);
			else opterr++;
		}
	printf ("%s", ENDARGS);
	while (optind < argc)
		printf (" \"%s\"", argv[optind++]);
	putchar ('\n');
	exit (opterr);
	}

#endif
SHAR_EOF
fi
if test -f 'makefile'
then
	echo shar: "will not over-write existing file 'makefile'"
else
cat << \SHAR_EOF > 'makefile'
CFLAGS=-O

OBJS=shar.o traverse.o getopt.o
SRCS=shar.c traverse.c getopt.c
DOCS=shar.1 traverse.3 getopt.3

PR=/usr/ucb/print
LINT=lint -h

shar: $(OBJS) makefile
	@cc $(CFLAGS) -o shar $(OBJS)

traverse:
	@cc -o traverse -DSTANDALONE traverse.c

archive: makefile $(SRCS) $(DOCS)
	@shar -a makefile $(SRCS) $(DOCS) > archive

print:
	@$(PR) makefile shar.c

lint:
	@$(LINT) $(SRCS)

clean:
	@-rm -f core a.out *.o
SHAR_EOF
fi
if test -f 'shar.1'
then
	echo shar: "will not over-write existing file 'shar.1'"
else
cat << \SHAR_EOF > 'shar.1'
.TH SHAR 1net "March 4, 1986" "UNIX User's Manual" "Wang Institute"
.SH NAME
shar \- create file storage archive for extraction by /bin/sh
.SH SYNOPSIS
.B shar
[-abcmsuv] [-p prefix] [-d delim] files > archive
.SH DESCRIPTION
.I shar
prints its input files with special command lines around them
to be used by the shell,
.I /bin/sh ,
to extract the files later.
The output can be filtered through the shell to
recreate copies of the original files.
.PP
.I shar
allows directories to be named, and
.I shar
prints the necessary commands
.ul
(mkdir & cd)
to create new directories and fill them.
.I shar
will not allow existing files to be over-written;
such files must be removed by the user extracting the files.
.SH OPTIONS
.de OP
.TP
.B -\\$1
..
.OP a
All the options.
The options:
.B "-v -c -b -p <tab>X"
are implied.
.OP b
Extract files into basenames so that files with absolute path names
are put into the current directory.
This option has strange effects when directories are archived.
.OP 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.
One reason why the sizes may not match is that
.I shar
will append a newline to complete incomplete last lines;
.I shar
prints a message that mentions added newlines.
Another reason why the sizes may not match is that some
network mail programs remove non-whitespace control characters.
.I shar
prints a message that mentions control characters to the extractor.
.OP d delim
Use this as the ``end of file'' delimiter instead of the default.
The only reason to change it is if you suspect a file
contains the default delimiter:
.B SHAR_EOF.
.OP m
Reset the exact protection modes of files when they are extracted
(using the
.I chmod
program).
By default, the extractor's default file modes are used,
and executable files (e.g., shell scripts)
are made executable.
.OP p prefix
Use this as the prefix to each line of the archived files.
This is to make sure that special characters at the start of lines 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,
though perhaps less safe if you don't know what is in the files.
.OP s
Silent running.
All checking and extra output is inhibited.
.OP u
Archive the input files with the
.I uuencode
format for later extraction with
.I uudecode.
This will allow you to send files with control characters in them,
but will slow down the extracting.
You must be sure that the receiving party has access to uudecode.
.OP 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.
.SH "SEE ALSO
sh(1), tar(1), cpio(1), tp(1), uuencode(1), uudecode(1)
.br
fpack(1) is a plain-file packer useful for UNIX and MSDOS
.SH AUTHOR
Gary Perlman
(based on a shell version by James Gosling,
with additions motivated by many people on the UNIX network:
Derek Zahn,
Michael Thompson,
H. Morrow Long,
Fred Avolio,
Gran Uddeborg,
Chuck Wegrzyn,
nucleus!randy@TORONTO,
&
Bill McKeeman)
.SH LIMITATIONS
.I shar
does not know anything about links between files.
SHAR_EOF
fi
if test -f 'shar.c'
then
	echo shar: "will not over-write existing file 'shar.c'"
else
cat << \SHAR_EOF > 'shar.c'
/*
	Shar puts readable text files together in a package
	from which they are extracted with /bin/sh and friends.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>

#ifndef lint
static	char	sccsid[] = "@(#)shar.c 2.0 (Usenet) 3/11/86";
#endif

typedef	int 	Boole;
#define	TRUE	((Boole) 1)
#define	FALSE	((Boole) 0)
typedef	int 	Status;
#define	SUCCESS	0
#define	FAILURE	1

/* GLOBALS */
int 	Lastchar;   /* the last character printed */
int 	Ctrlcount;  /* how many bad control characters are in file */

/* COMMANDS */
#define	EXTRACT "#! /bin/sh"          /* magic exec string at shar file start */
#define	PATH    "/bin:/usr/bin:$PATH" /* search path for programs */
#define	CAT     "cat";                /* /bin/cat */
#define	SED     "sed 's\/^%s\/\/'"    /* /bin/sed removes Prefix from lines */
#define	MKDIR   "mkdir"               /* make a new dirctory */
#define	CHMOD   "chmod"               /* change file mode */
#define	CHDIR   "cd"                  /* change current directory */
#define	TEST    "test"                /* /bin/test files */
#define	WC_C    "wc -c <"             /* counts chars in file */
#define	ECHO    "echo shar:"          /* echo a message to extractor */
#define	DECODE  "uudecode"            /* used to decode uuencoded files */

/*FUNCTION main: traverse files to make archive to standard output */
main (argc, argv) char **argv;	
	{
	int 	shar ();
	int 	optind;

	if ((optind = initial (argc, argv)) < 0)
		exit (FAILURE);

	if (header (argc, argv, optind))
		exit (FAILURE);

	while (optind < argc)
		traverse (argv[optind++], shar);

	footer ();

	exit (SUCCESS);
	}

/* OPTIONS */
Boole	Verbose = FALSE;       /* provide append/extract feedback */
Boole	Basename = FALSE;      /* extract into basenames */
Boole	Count = FALSE;         /* count characters to check transfer */
Boole	Overcheck = TRUE;      /* check overwriting */
Boole	Uucode = FALSE;        /* uuencode the file */
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 */
Boole	Modeset = FALSE;       /* set exact modes on extraction */

/*FUNCTION: initial: do option parsing and any setup */
int /* returns the index of the first operand file */
initial (argc, argv) char **argv;
	{
	int 	errflg = 0;
	extern	int 	optind;
	extern	char	*optarg;
	int 	C;
	char	*optstring = "abcmsuvp:d:";
	char	*usage = "[-abcmsuv] [-p prefix] [-d delim] files > archive";

	while ((C = getopt (argc, argv, optstring)) != EOF)
		switch (C)
			{
			case 'u': Uucode = TRUE; break;
			case 'b': Basename = TRUE; break;
			case 'c': Count = TRUE; break;
			case 'd': Delim = optarg; break;
			case 'm': Modeset = TRUE; break;
			case 's': /* silent running */
				Overcheck = FALSE;
				Verbose = FALSE;
				Count = FALSE;
				Prefix = NULL;
				break;
			case 'a': /* all the options */
				Verbose = TRUE;
				Count = TRUE;
				Basename = TRUE;
				/* fall through to set prefix */
				optarg = "	X";
				/* FALLTHROUGH */
			case 'p': (void) sprintf (Filter, SED, Prefix = optarg); break;
			case 'v': Verbose = TRUE; break;
			default: errflg++;
			}

	if (errflg || optind == argc)
		{
		if (optind == argc)
			fprintf (stderr, "shar: No input files\n");
		fprintf (stderr, "Usage: shar %s\n", usage);
		return (-1);
		}

	return (optind);
	}

/*FUNCTION header: print header for archive */
header (argc, argv, optind)
char	**argv;
	{
	int 	i;
	Boole	problems = FALSE;
	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 (FAILURE);

	printf ("%s\n", EXTRACT);
	printf ("# This is a shell archive, meaning:\n");
	printf ("# 1. Remove everything above the %s line.\n", EXTRACT);
	printf ("# 2. Save the resulting text in a file.\n");
	printf ("# 3. Execute the file with /bin/sh (not csh) to create:\n");
	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 : "");
	printf ("export PATH; PATH=%s\n", PATH);
	return (SUCCESS);
	}

/*FUNCTION footer: print end of shell archive */
footer ()
	{
	printf ("exit 0\n");
	printf ("#\tEnd of shell archive\n");
	}

/* uuencode options available to send cntrl and non-ascii chars */
/* really, this is getting to be too much like cpio or tar */

/* ENC is the basic 1 character encoding function to make a char printing */
#define ENC(c) (((c) & 077) + ' ')

/*FUNCTION uuarchive: simulate uuencode to send files */
Status
uuarchive (input, protection, output)
char	*input;
int 	protection;
char	*output;
	{
	FILE	*in;
	if (in = fopen (input, "r"))
		{
		printf ("%s << \\%s\n", DECODE, Delim);
		printf ("begin %o %s\n", protection, output);
		uuencode (in, stdout);
		printf ("end\n");
		fclose (in);
		return (SUCCESS);
		}
	return (FAILURE);
	}

uuencode (in, out)
FILE *in, *out;
	{
	char	buf[80];
	int 	i, n;
	for (;;)
		{
		n = fread (buf, 1, 45, in);
		putc (ENC(n), out);
		for (i = 0; i < n; i += 3)
			outdec (&buf[i], out);
		putc ('\n', out);
		if (n <= 0)
			break;
		}
	}

/* output one group of 3 bytes, pointed at by p, on file ioptr */
outdec (p, ioptr)
char	*p;
FILE	*ioptr;
	{
	int c1, c2, c3, c4;
	c1 = *p >> 2;
	c2 = (*p << 4) & 060 | (p[1] >> 4) & 017;
	c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03;
	c4 = p[2] & 077;
	putc (ENC(c1), ioptr);
	putc (ENC(c2), ioptr);
	putc (ENC(c3), ioptr);
	putc (ENC(c4), ioptr);
	}

/*FUNCTION archive: make archive of input file to be extracted to output */
archive (input, output)
char	*input, *output;
	{
	char	buf[BUFSIZ];
	FILE	*ioptr;

	if (ioptr = fopen (input, "r"))
		{
		Ctrlcount = 0;    /* no bad control characters so far */
		Lastchar = '\n';  /* simulate line start */

		printf ("%s << \\%s > '%s'\n", Filter, Delim, output);

		if (Prefix)
			{
			while (fgets (buf, BUFSIZ, ioptr))
				{
				if (Prefix) outline (Prefix);
				outline (buf);
				}
			}
		else copyout (ioptr);

		if (Lastchar != '\n') /* incomplete last line */
			putchar ('\n');   /* Delim MUST begin new line! */

		if (Count == TRUE && Lastchar != '\n')
			printf ("%s \"a missing newline was added to '%s'\"\n", ECHO, input);
		if (Count == TRUE && Ctrlcount)
			printf ("%s \"%d control character%s may be missing from '%s'\"\n",
				ECHO, Ctrlcount, Ctrlcount > 1 ? "s" : "", input);

		(void) fclose (ioptr);
		return (SUCCESS);
		}
	else
		{
		fprintf (stderr, "shar: Can't open '%s'\n", input);
		return (FAILURE);
		}
	}

/*FUNCTION copyout: copy ioptr to stdout */
/*
	Copyout copies its ioptr almost as fast as possible
	except that it has to keep track of the last character
	printed.  If the last character is not a newline, then
	shar has to add one so that the end of file delimiter
	is recognized by the shell.  This checking costs about
	a 10% difference in user time.  Otherwise, it is about
	as fast as cat.  It also might count control characters.
*/
#define	badctrl(c) (iscntrl (c) && !isspace (c))
copyout (ioptr)
register	FILE	*ioptr;
	{
	register	int 	C;
	register	int 	L;
	register	count;

	count = Count;

	while ((C = getc (ioptr)) != EOF)
		{
		if (count == TRUE && badctrl (C))
			Ctrlcount++;
		L = putchar (C);
		}

	Lastchar = L;
	}

/*FUNCTION outline: output a line, recoring last character */
outline (s)
register	char	*s;
	{
	if (*s)
		{
		while (*s)
			{
			if (Count == TRUE && badctrl (*s)) Ctrlcount++;
			putchar (*s++);
			}
		Lastchar = *(s-1);
		}
	}

/*FUNCTION shar: main archiving routine passed to directory traverser */
shar (file, type, pos)
char	*file;     /* file or directory to be processed */
int 	type;      /* either 'f' for file or 'd' for directory */
int 	pos;       /* 0 going in to a file or dir, 1 going out */
	{
	struct	stat	statbuf;
	char	*basefile = file;
	int 	protection;

	if (!strcmp (file, "."))
		return;

	if (stat (file, &statbuf))
		statbuf.st_size = 0;
	else
		protection = statbuf.st_mode & 07777;

	if (Basename == TRUE)
		{
		while (*basefile) basefile++; /* go to end of name */
		while (basefile > file && *(basefile-1) != '/')
			basefile--;
		}

	if (pos == 0) /* before the file starts */
		{
		beginfile (basefile, type, statbuf.st_size, protection);
		if (type == 'f')
			{
			if (Uucode)
				{
				if (uuarchive (file, protection, basefile) != SUCCESS)
					exit (FAILURE);
				}
			else
				if (archive (file, basefile) != SUCCESS)
					exit (FAILURE);
			}
		}
	else /* pos == 1, after the file is archived */
		endfile (basefile, type, statbuf.st_size, protection);
	}

/*FUNCTION beginfile: do activities before packing up a file */
beginfile (basefile, type, size, protection)
char	*basefile;  /* name of the target file */
int 	type;       /* either 'd' for directory, or 'f' for file */
int 	size;       /* size of the file */
int 	protection; /* chmod protection bits */
	{
	if (type == 'd')
		{
		printf ("if %s ! -d '%s'\n", TEST, basefile);
		printf ("then\n");
		if (Verbose == TRUE)
			printf ("	%s \"creating directory '%s'\"\n", ECHO, basefile);
		printf ("	%s '%s'\n", MKDIR, basefile);
		printf ("fi\n");
		if (Verbose == TRUE)
			printf ("%s \"entering directory '%s'\"\n", ECHO, basefile);
		printf ("%s '%s'\n", CHDIR, basefile);
		}
	else /* type == 'f' */
		{
		if (Verbose == TRUE)
			printf ("%s \"extracting '%s'\" '(%d character%s)'\n",
				ECHO, basefile, size, size > 1 ? "s" : "");

		if (Overcheck == TRUE)
			{
			printf ("if %s -f '%s'\n", TEST, basefile);
			printf ("then\n");
			printf ("	%s \"will not over-write existing file '%s'\"\n",
				ECHO, basefile);
			printf ("else\n");
			}

		}
	}

/*FUNCTION endfile: do activities after packing up a file */
endfile (basefile, type, size, protection)
char	*basefile;  /* name of the target file */
int 	type;       /* either 'd' for directory, or 'f' for file */
int 	size;       /* size of the file */
int 	protection; /* chmod protection bits */
	{
	if (type == 'd')
		{
		if (Modeset == TRUE)
			printf ("%s %o .\n", CHMOD, protection);
		if (Verbose == TRUE)
			printf ("%s \"done with directory '%s'\"\n", ECHO, basefile);
		printf ("%s ..\n", CHDIR);
		}
	else /* type == 'f' (plain file) */
		{
		printf ("%s\n", Delim);
		if (Count == TRUE)
			{
			printf ("if %s %d -ne \"`%s '%s'`\"\n", TEST, size, WC_C, basefile);
			printf ("then\n");
			printf ("	%s \"error transmitting '%s'\" ", ECHO, basefile);
			printf ("'(should have been %d character%s)'\n",
				size, size == 1 ? "" : "s");
			printf ("fi\n");
			}
		if (Uucode == FALSE) /* might have to chmod by hand */
			{
			if (Modeset == TRUE) /* set all protection bits (W McKeeman) */
				printf ("%s %o '%s'\n",
						CHMOD, protection, basefile);
			else if (protection & 0111) /* executable -> chmod +x */
				printf ("%s +x '%s'\n", CHMOD, basefile);
			}
		if (Overcheck == TRUE)
			printf ("fi\n");
		}
	}
SHAR_EOF
fi
if test -f 'traverse.3'
then
	echo shar: "will not over-write existing file 'traverse.3'"
else
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.
This traversal is in depth first order
so that files are processed in the order
that they are stored in the directory.
.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
fi
if test -f 'traverse.c'
then
	echo shar: "will not over-write existing file 'traverse.c'"
else
cat << \SHAR_EOF > 'traverse.c'
/*LINTLIBRARY*/
#ifndef lint
static	char	sccsid[] = "@(#)traverse.c 1.0 (WangInst) 12/23/84";
#endif

#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
fi
cd ..
exit 0
#	End of shell archive

bet@dukeac.UUCP (Bennett Todd) (03/28/89)

Yikes! 25K and more of sharchive, to distribute a shar program! No doubt, for
someone running a major distribution operation the extensive features would be
worthwhile... But for those of us who rarely if ever try to make our shar
utility handle subdirectories, binary files, mailer length limits, or any of
the other amusing perversions you can hack around, allow me to offer the
following:

: shar -- make a shell archive of the files from the command line
echo ": This is a sharchive -- extract the files by running through sh."
for f
do
	echo "echo x - $f"
	echo "sed 's/^X//' <<\Shar_Eof >$f"
	sed 's/^/X/' <$f
	echo "Shar_Eof"
done
echo "exit 0"

Don't try to pack subdirectories, don't try to pack files containing arbitrary
non-text data, and manually invoke shar separately on sets of files each
smaller than your transmission limit, and this will serve just fine.

-Bennett