[net.sources] shar.c

perlman@wivax.UUCP (12/16/84)

Here we go again.  I've gotten enough sugestions for my shar.c
that I am posting a new version.  The minor changes are that
some potential bugs were removed along with unused features.
The major change is that shar now knows how to march through
a directory hierarchy.  This posting should supersede the
versions posted recently.

The traversal is done with a function for marching through directories.
I hope people find it useful for other tasks.  I am pretty sure about
its behaviour on pre-Berkeley 4.2 UNIX, but not positive about it
on Berkeley 4.2.  If there are any bugs in it, please send fixes to me.

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

shar -v shar.1 shar.c makefile traverse.3 traverse.c
-----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
#	makefile
#	traverse.3
#	traverse.c
echo shar: extracting shar.1
cat - << \SHAR_EOF > shar.1
.TH SHAR 1net "December 16, 1984"
.SH NAME
shar \- create storage archive for extraction by /bin/sh
.SH SYNOPSIS
.B shar
[-v] files
.SH DESCRIPTION
shar prints its input files with special lines around them
to be used by the shell (/bin/sh) to extract them later.
The output can be filtered through the shell to
recreate copies of the original files.
The -v (verbose) option causes feedback messages about
what shar is doing to be printed during extraction.
.PP
shar allows directories to be named,
and shar prints the necessary commands to create
new directories and fill them.
.SH AUTHOR
Gary Perlman
(based on a shell version by James Gosling)
.SH BUGS
shar does not know anything about links between files,
about binary files,
or about overwriting existing files.
SHAR_EOF
echo shar: extracting shar.c
cat - << \SHAR_EOF > shar.c
#include <stdio.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 a -v (verbose) option.

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

Some enhancements motivated by Michael Thompson.
	I did not put in his feature of using sed to strip
	a prefix character because: (1) I did not want the
	shar scripts to depend on sed because some systems
	do not have it.  (2) The convention arose so that
	people would not have to worry about the end of input
	delimiter being part of the line. This is unlikely
	and better handled by having a programmable delimiter.
	I don't think even that is necessary.

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.
*/


#define	DELIM           "SHAR_EOF"  /* put after each file */
#define	SHAR            "shar"      /* the name of this program */
#define	READ_PERMISSION 4           /* access permission */

int 	Verbose = 0; /* option to provide append/extract feedback */

main (argc, argv) char **argv;	
	{
	int 	status = 0;
	if (!strcmp (argv[1], "-v"))
		{
		Verbose = 1;
		argc--;
		argv++;
		}
	if (argc == 1)
		{
		fprintf (stderr, "%s: No input files\n", SHAR);
		fprintf (stderr, "USAGE: %s [-v] files > archive\n", SHAR);
		exit (1);
		}
	if (header (argc, argv))
		exit (2);
	while (--argc)
		status += shar (*++argv);
	footer ();
	exit (status);
	}

header (argc, argv)
char	**argv;
	{
	int 	i;
	int 	problems = 0;
	for (i = 1; i < argc; i++)
		if (access (argv[i], READ_PERMISSION))
			{
			fprintf (stderr, "%s: Can't read %s\n", SHAR, argv[i]);
			problems++;
			}
	if (problems) return (problems);
	puts ("-----cut here-----cut here-----cut here-----cut here-----");
	puts ("#!/bin/sh");
	printf ("# %s:	Shell Archiver\n", SHAR);
	puts ("#\tRun the following text with /bin/sh to create:");
	for (i = 1; i < argc; i++)
		printf ("#\t%s\n", argv[i]);
	return (0);
	}

footer ()
	{
	puts ("#That's all folks!");
	puts ("exit 0");
	}

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

shar (file)
char	*file;
	{
	int 	rshar ();
	traverse (file, rshar);
	}

rshar (file, type, place)
	{
	if (!strcmp (file, ".")) return;
	if (place == 0)
		{
		if (type == 'd')
			{
			if (Verbose)
				printf ("echo %s: creating directory %s\n", SHAR, file);
			printf ("mkdir %s\n", file);
			printf ("chdir %s\n", file);
			}
		else /* type == 'f' */
			{
			if (Verbose)
				printf ("echo %s: extracting %s\n", SHAR, file);
			archive (file);
			}
		}
	else /* place == 1 */
		{
		if (type == 'd')
			{
			if (Verbose)
				printf ("echo %s: done with directory %s\n", SHAR, file);
			printf ("chdir ..\n");
			}
		}
	}
SHAR_EOF
echo shar: extracting makefile
cat - << \SHAR_EOF > makefile
# The SYSTEM variable should be set to BSD4_2 to use the new routines
# specific to that release.  Otherwise, the value doesn't matter.
SYSTEM=BSD4_1
CFLAGS=-O
shar: shar.o traverse.o
	cc -o shar shar.o traverse.o
traverse.o: traverse.c
	cc -D$(SYSTEM) -c $(CFLAGS) traverse.c
SHAR_EOF
echo shar: extracting traverse.3
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
echo shar: extracting traverse.c
cat - << \SHAR_EOF > traverse.c
/*LINTLIBRARY*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>

#ifdef BSD4_2

#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);
	}
SHAR_EOF
#That's all folks!
exit 0