[comp.sys.ibm.pc] BSD Directory Interface for MSDOS

mohsen@retix.retix.COM (Mohsen Banan) (08/04/87)

>>  Subject: bsd unix directory utilities wanted for msdos
>>  Keywords: opendir, readdir, telldir, seekdir, rewinddir, closedir, for MSDOS


>>  	Has anyone out there rewritten the UNIX bsd directory
>>  scanning utilities to work for msdos? Are there any companies
>>  that sell libaries of unix compatable "procedures", but for MSDOS?
>>  How sells the best? We use Micro Soft "C".

>>  	Thanks in advance.
>>  	Neville

>>  address:
>>  	calgary.uucp!vuwcomp!frc!ncd (Neville Dempsey)

Last week-end, I ported a few UNIX utilities to MSDOS.
In the process of doing so I ended up writing 
BSD style directory interface for MSDOS.

I have not tested them much and they are not optimized.
They appear to do the job, however.

The following shar file contains four directories.
    include:	Guess!
    dossup:     DOS directory interfcae, getopt, traverse
    shar:	A port of shar to MSDOS. Uses dossup.
    lsr:	A simple recursive file listing utility. Uses dossup.

You need to use MSC to compile every thing.

 If you make improvements to the BSD directory interface for MSDOS,
 please send me a copy.

 Mohsen Banan.
 retix!mohsen


#! /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 the files:
#	include
#	dossup
#	lsr
#	shar
# This archive created: Tue Aug  4 11:07:39 1987
export PATH; PATH=/bin:$PATH
if test ! -d 'include'
then
	mkdir 'include'
fi
cd 'include'
if test -f 'eh.h'
then
	echo shar: will not over-write existing file "'eh.h'"
else
cat << \SHAR_EOF > 'eh.h'
/*+
 * Description:
 * Exception Handler Module Header
 *
-*/

/*
 * Author: Mohsen Banan.
 * History:
 * 
 */

/*
 * SCCS Revision: @(#)eh.h	1.1    Released: 8/3/87
 */

#ifndef _EH_H_	/*{*/
#define _EH_H_


#define	EH_assert(expr)\
	if (!(expr)) {\
		fprintf(stderr,"Assertion failed: file %s, line %d\n", __FILE__, __LINE__);\
	}
	
#ifndef EH_oops	/*{*/
#define EH_oops() fprintf(stderr,"OOPS: file %s, line %d\n", __FILE__, __LINE__)
#endif	/*}*/

#ifndef EH_fatal
#define EH_fatal(str)  fprintf(stderr,"FATAL: %s, %d: %s\n", __FILE__, __LINE__, (str)); exit(1);
#endif

#ifndef EH_problem
#define EH_problem(str)  fprintf(stderr,"PROBLEM: %s, %d: %s\n", __FILE__, __LINE__, (str))
#endif

#ifndef EH_violation
#define EH_violation(str)  fprintf(stderr,"VIOLATION: %s, %d: %s\n", __FILE__, __LINE__, (str))
#endif

#endif /*}*/
SHAR_EOF
fi # end of overwriting check
if test -f 'estd.h'
then
	echo shar: will not over-write existing file "'estd.h'"
else
cat << \SHAR_EOF > 'estd.h'
/*+
 * Description:
 *
 *
-*/

/*
 * Author: Mohsen Banan.
 * History:
 * 
 */

/*
 * SCCS Revision: @(#)estd.h	1.1    Released: 8/3/87
 */

#ifndef _ESTD_H_	/*{*/
#define _ESTD_H_

/*
 * The purpose of the standard definitions include file is to provide
 * consistent datatype definitions across different environments.
 * Thus, a SmInt (small integer)  is always 8 bits, no matter which machine 
 * it is compiled on. In the same, way a MdInt (medium integer) is always
 * 16 bits and LgInt (large integer) is always 32 bits.
 *
 */

#define SMSIZE	8	        /* 8 bits in "small" */
#define MDSIZE	16	        /* 16 bits in "medium" */
#define LGSIZE	32           	/* 32 bits in "large" */

typedef char SmInt;	        /* SMSIZE-bit signed integer */
typedef short MdInt;		/* MDSIZE-bit signed integer */
typedef long LgInt;		/* LGSIZE-bit signed integer */

typedef unsigned char SmUns;	/* SMSIZE-bit unsigned integer */
typedef unsigned short MdUns;	/* MDSIZE-bit unsigned integer */
typedef unsigned long LgUns;	/* LGSIZE-bit unsigned integer */

typedef unsigned char SmBits;	/* SMSIZE-bit bit string */
typedef unsigned short MdBits;	/* MDSIZE-bit bit string */
typedef unsigned long LgBits;	/* LGSIZE-bit bit string */

typedef int Int;		/* generic integer */
typedef unsigned int Uns;       /* generic unsigned integer */

typedef int Bool;		/* boolean, Int true or false variable */
#ifndef FALSE
#define FALSE 0			/* Logical false, value 0 */
#endif
#ifndef TRUE
#define TRUE 1			/* Logical true, value 1  */
#endif

typedef int SuccFail;	    /* Typical return Values */
#ifndef SUCCESS
#define SUCCESS 0
#endif
#ifndef FAIL
#define FAIL    (-1)
#endif

typedef unsigned char Byte;	/* SMSIZE, smallest unit of addressable store */
typedef char Char;		/* Single ASCII character */
typedef Char * String;		/* Pointer to null-terminated charSequence */

typedef char * Ptr;		/* pointer to arbitrary type */
typedef LgUns Arg; 		/* Uninterpreted LGSIZE-bit word */
#ifndef NULL
#define NULL 0			/* universal empty result */
#endif
#ifndef Void
#define Void void		/* null return type of procedures */
#endif

#define STATIC  static      /* Names not needed outside this src module  */

#ifndef LOCAL
#define LOCAL               /* Names not needed outside this software module */
#endif
#define LCL_XTRN extern     /* Names defines within this software module */

#ifndef PUBLIC
#define PUBLIC              /* Names needed outside this software module */
#endif
#define EXTERN  extern      /* Names defined outside this software module */

#define DIMOF(array) (sizeof array / sizeof array[0])
#define ENDOF(array) (array - 1 + (sizeof array / sizeof array[0]))

#ifndef OOPS
#define OOPS()  fprintf(stderr,"OOPS: file %s, line %d\n", __FILE__, __LINE__)
#endif
#ifndef PROBLEM
#define PROBLEM()  fprintf(stderr,"PROBLEM: file %s, line %d\n", __FILE__, __LINE__)
#endif
#ifndef TRACE
#define TRACE()  fprintf(stderr,"TRACE: file %s, line %d\n", __FILE__, __LINE__)
#endif

#endif	/*}*/
SHAR_EOF
fi # end of overwriting check
if test ! -d 'sys'
then
	mkdir 'sys'
fi
cd 'sys'
if test -f 'dir.h'
then
	echo shar: will not over-write existing file "'dir.h'"
else
cat << \SHAR_EOF > 'dir.h'
/*+
 * Description:
 *	Simulation header file for BSD style directory facilities
 *	in DOS environment.
 *
-*/

/*
 * Author: Mohsen Banan.
 * History:
 * 
 */

/*
 * SCCS Revision: @(#)dir.h	1.2    Released: 8/3/87
 */

#ifdef MSDOS	/*{*/

#ifndef _DIR_H_	/*{*/
#define _DIR_H_


#define MAXNAMLEN	13

struct	direct {
	int	d_namlen;
	char	d_name[MAXNAMLEN];
};

typedef enum DirState {
    DirActive,		/* While consecutive readdirs */ 
    DirVirgin,		/* Very first time when opendir */
    DirInactive		/* When (DIR *) stream changes */
} DirState;

typedef struct {
    char dirName[128];	/* Name of the directory, including drive */
    char *dirNameEnd;	/* End of String Location for dirName */
    DirState state;	/* See DirState */
    long index;		/* Position of a file name inside the directory */
    char indexName[MAXNAMLEN];  /* File Name corresponding to indexName */
} DIR ;

extern	DIR *opendir();
extern	struct direct *readdir();
extern	long telldir();
extern	void seekdir();
#define rewinddir(dirp)	seekdir((dirp), (long)0)
extern	void closedir();

#endif	/*}*/
#else
#include "/usr/include/sys/dir.h"
#endif	/*}*/
SHAR_EOF
fi # end of overwriting check
cd ..
cd ..
if test ! -d 'dossup'
then
	mkdir 'dossup'
fi
cd 'dossup'
if test -f 'bsddir.c'
then
	echo shar: will not over-write existing file "'bsddir.c'"
else
cat << \SHAR_EOF > 'bsddir.c'
/*+
 * Description:
 * This file contains a series of functions that provide for
 * a BSD style directory interface in MSDOS envirnment.
 *
-*/

/*
 * Author: Mohsen Banan.
 * History:
 *
 */

#ifdef SCCS_VER	/*{*/
static char sccs[] = "@(#)bsddir.c	1.1    Released: 8/3/87";
#endif /*}*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <ctype.h>
#include <sys/stat.h>
#include <dos.h>
#include <direct.h>
#include "eh.h"
#include "dosos.h"
#include <malloc.h>

typedef struct DirsCB {
    DirDta dirDta;
    DIR *lastActive;
    int opensCount;
} DirsCB;

static DirsCB dirsCB;
static char  cwd[128];


/*<
 * Function:
 * Description:
 * Given a path to a directory, associate a directory
 * stream with it. Opendir returns a pointer to be used 
 * in all subsequent operations on this directory.
 *
 * Arguments:
 *	fileName: Path to the directory.
 * Returns:
 *	If directory is accessible, and there is enough memory
 *	to be allocated for processing, a (DIR *) is returned that
 *	must be used in all subsequent operations.
 *	Otherwize NULL is returned.
>*/
DIR *
opendir(fileName)
char *fileName;		/* path to the directory */
{
    DIR *thisDir;
    int i;
    struct stat	buf;
    extern char *getAbsPath();
#define	isdir(path) (stat(path, &buf) ? 0 : (buf.st_mode&S_IFMT)==S_IFDIR)

#if 0
    printf("opendir fileName=%s\n", fileName);
#endif
    if ( ! isdir(fileName) ) {
	EH_problem("Bad argument");
 	return ( (DIR *) 0);
    }
    if ( strlen(fileName) >= sizeof(thisDir->dirName) ) {
	EH_problem("Bad argument");
	return ( (DIR *) 0);
    }

    thisDir = (DIR *)malloc(sizeof(*thisDir));
    if ( ! thisDir ) {
	EH_problem("Malloc");
	return ( thisDir );
    }

    /* Keep Dir Name, It may be needed Later */
    strcpy(thisDir->dirName, getAbsPath(fileName));
    i = strlen(thisDir->dirName);
    thisDir->dirNameEnd = thisDir->dirName + i;

    thisDir->state = DirVirgin;
    thisDir->index = 0;
    strcpy(thisDir->indexName, "*.*");

    return ( thisDir );
}
    

/*<
 * Function:
 * Description:
 *	Reads in the next directory entry.
 * Arguments:
 *
 * Returns:
 *	A pointer to the next directory entry.
 *	Uppon reaching the end of the directory, NULL is returned.
 *
>*/
struct direct *
readdir( dirp )
DIR *dirp;
{
    static struct direct dir;
    struct direct *retVal;
    char * srcPtr;
    char * dstPtr;

    if (getcwd(cwd, sizeof(cwd)) == (char *)0) {
	perror("getcwd");
        exit(1);
    }
    if (chdir(dirp->dirName)) {
	perror("getcwd");
        exit(1);
    }
    
    switch ( dirp->state ) {
    case DirInactive:
	if ( findLoc(dirp->index, &dirsCB.dirDta) ) {
	    retVal = (struct direct *) 0;
            goto done;
	}
	if ( dirsCB.lastActive ) {
	    dirsCB.lastActive->state = DirInactive;
	}
	dirsCB.lastActive = dirp;
	dirp->state = DirActive;
	/* Now it is as if it was active, 
	 * Also execute DirActive Code, do NOT break.
	 */
	
    case DirActive:
	if ( findNext(&dirsCB.dirDta) ) {
	    retVal = (struct direct *) 0;
            goto done;
	}
	break;

    case DirVirgin:
	if ( findFirst(dirp->indexName, &dirsCB.dirDta, 0x3F) ) {
	    retVal = (struct direct *) 0;
            goto done;
	}
	if ( dirsCB.lastActive ) {
	    dirsCB.lastActive->state = DirInactive;
	}
	dirsCB.lastActive = dirp;
	dirp->state = DirActive;
	break;

    default:
	EH_oops();
	break;
    }

    /* Now dirsCB.dirDta contains the information we need */
    dirp->index++;
    strcpy(dirp->indexName, dirsCB.dirDta.fname);

    /* Do strcpy and change the case to lower */
    for (srcPtr=dirsCB.dirDta.fname, dstPtr=dir.d_name; *srcPtr;
	  ++srcPtr, ++dstPtr) {
	*dstPtr = tolower(*srcPtr);
    }
    *dstPtr = *srcPtr;	/* '\0' terminate it */

    dir.d_namlen = strlen(dirsCB.dirDta.fname);
    retVal = &dir;

done:
    if (chdir(cwd)) {
	perror("chdir");
        exit(1);
    }
	
    return ( retVal );
	
}


/*<
 * Function:
 * Description:
 *	Closes the named directory and frees the structure
 *	associated with it.
 *
>*/
void
closedir( dirp )
DIR *dirp;
{
    if ( dirp->state == DirActive ) {
	dirsCB.lastActive = (DIR *)0;
    }
    free(dirp);
}


/*<
 * Function:
 * Description:
 *	Report the current location associated with the named directory.
 *
>*/
long
telldir( dirp )
DIR *dirp;
{
      return ((long) dirp->index);
}


/*<
 * Function:
 * Description:
 *	Sets the position of the next readdir operation.
 *
>*/
void
seekdir( dirp, loc )
DIR *dirp;
long loc;
{
        dirp->index = loc;
}


/*<
 * Function:
 * Description:
 *	Given a relative path, find the absolute path.
 *	Returns a pointer to the absolute path.
 *	See MSC Lib for format of getcwd.
 *
>*/
char *
getAbsPath(fileName)
char *fileName;
{
    static char absPath[128];

    if (getcwd(cwd, sizeof(cwd)) == (char *)0) {
	perror("getcwd");
        exit(1);
    }
    if (chdir(fileName)) {
	perror("chdir");
        exit(1);
    }
    if (getcwd(absPath, sizeof(absPath)) == (char *)0) {
	perror("getcwd");
        exit(1);
    }
    if (chdir(cwd)) {
	perror("chdir");
        exit(1);
    }
    return (absPath);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'build.bat'
then
	echo shar: will not over-write existing file "'build.bat'"
else
cat << \SHAR_EOF > 'build.bat'
 msc -DMSDOS  -I..\include   dosdir.c;
 msc -DMSDOS  -I..\include   bsddir.c;
 msc -DMSDOS  -I..\include   getopt.c;
 msc -DMSDOS  -I..\include   traverse.c;
del  dossup.lib
 lib  dossup    dosdir.obj bsddir.obj getopt.obj traverse.obj;
SHAR_EOF
fi # end of overwriting check
if test -f 'dir.h'
then
	echo shar: will not over-write existing file "'dir.h'"
else
cat << \SHAR_EOF > 'dir.h'
/*+
 * Description:
 *	Simulation header file for BSD style directory facilities
 *	in DOS environment.
 *
-*/

/*
 * Author: Mohsen Banan.
 * History:
 * 
 */

/*
 * SCCS Revision: @(#)dir.h	1.2    Released: 8/3/87
 */

#ifdef MSDOS	/*{*/

#ifndef _DIR_H_	/*{*/
#define _DIR_H_


#define MAXNAMLEN	13

struct	direct {
	int	d_namlen;
	char	d_name[MAXNAMLEN];
};

typedef enum DirState {
    DirActive,		/* While consecutive readdirs */ 
    DirVirgin,		/* Very first time when opendir */
    DirInactive		/* When (DIR *) stream changes */
} DirState;

typedef struct {
    char dirName[128];	/* Name of the directory, including drive */
    char *dirNameEnd;	/* End of String Location for dirName */
    DirState state;	/* See DirState */
    long index;		/* Position of a file name inside the directory */
    char indexName[MAXNAMLEN];  /* File Name corresponding to indexName */
} DIR ;

extern	DIR *opendir();
extern	struct direct *readdir();
extern	long telldir();
extern	void seekdir();
#define rewinddir(dirp)	seekdir((dirp), (long)0)
extern	void closedir();

#endif	/*}*/
#else
#include "/usr/include/sys/dir.h"
#endif	/*}*/
SHAR_EOF
fi # end of overwriting check
if test -f 'dosdir.c'
then
	echo shar: will not over-write existing file "'dosdir.c'"
else
cat << \SHAR_EOF > 'dosdir.c'
/*+
 * Description:
 * 	Basic DOS directory operations.
 *
-*/

/*
 * Author: Mohsen Banan.
 * History:
 *
 */

#ifdef SCCS_VER	/*{*/
static char sccs[] = "@(#)dosdir.c	1.1    Released: 8/3/87";
#endif /*}*/

#include <stdio.h>
#include <dos.h>
#include "dosos.h"
#include <sys/dir.h>


/*<
 * Function:
 * Description:
 * findFirst - find first file in chosen directory 
 *
 * Arguments:
 *
 * Returns:
 *
>*/
int
findFirst(path, dirDta, mask)
char *path;
DirDta  *dirDta;
unsigned int mask;
{
    union REGS inRegs;
    union REGS outRegs;
    struct SREGS segRegs;
    struct DOSERROR dosErr;

    inRegs.x.ax = 0x1A00;	/* DOS interrupt 1A */
    inRegs.x.dx = GET_OFF(dirDta);
    segRegs.ds = GET_SEG(dirDta);
    
    intdosx(&inRegs, &outRegs, &segRegs);    /* Set Data Transfer Address */

    inRegs.x.ax = 0x4E00;	/* DOS interrupt 4E */
    inRegs.x.cx = mask;
    inRegs.x.dx = GET_OFF(path);
    segRegs.ds = GET_SEG(path);
    intdosx(&inRegs, &outRegs, &segRegs);    /* dirDta contains the result */

    if ( outRegs.x.cflag ) {
	dosexterr(&dosErr);
	/* Code 2 is File Not Found */
	if ( dosErr.exterror != 2 ) {
	    printf("exterror=%d, class=%d, action=%d, locus=%d\n",
	        dosErr.exterror, dosErr.class, dosErr.action, dosErr.locus);
	}
	return ( -1 );
    }
    return ( 0 );
}




/*<
 * Function:
 * Description:
 * findNext - find the next file in the same directory
 *
 * Arguments:
 *
 * Returns:
 *
>*/

findNext( dirDta )
DirDta *dirDta;
{
    union REGS inRegs;
    union REGS outRegs;
    struct SREGS segRegs;
    struct DOSERROR dosErr;

    inRegs.x.ax = 0x1A00;	/* DOS interrupt 1A */
    inRegs.x.dx = GET_OFF(dirDta);
    segRegs.ds = GET_SEG(dirDta);
    
    intdosx(&inRegs, &outRegs, &segRegs);    /* Set Data Transfer Address */

    inRegs.x.ax = 0x4F00;	/* DOS interrupt 4E */
    intdosx(&inRegs, &outRegs, &segRegs);    /* dirDta contains the result */

    if ( outRegs.x.cflag ) {
	dosexterr(&dosErr);
	/* Code 18 is No more files */
	if ( dosErr.exterror != 18 ) {
	    printf("exterror=%d, class=%d, action=%d, locus=%d\n",
	        dosErr.exterror, dosErr.class, dosErr.action, dosErr.locus);
	}
	return ( -1 );
    }
    return ( 0 );
}


/*<
 * Function:
 * Description:
 *	This is a substitute for findFirst.
 *	I couldn't get it to work the way the dicumentation says it should
 *	work.
 *
 * Returns:
 *
>*/
int
findPath(path, dirDta)
char *path;
DirDta  *dirDta;
{
    if ( findFirst("*.*", dirDta, 0x3F) ) {
	return ( -1 );
    }

    while ( 1 ) {
	if  ( findNext(dirDta) ) {
             return ( -1 );
        }
        if ( ! strcmp(path, dirDta->fname) ) {
             return ( 0 );
        }
    }
}


/*<
 * Function:
 * Description:
 *	Seek to the loaction spercified by loc.
 *
 * Returns:
 *
>*/
int
findLoc(loc, dirDta)
long loc;
DirDta  *dirDta;
{
    long i;
    if ( findFirst("*.*", dirDta, 0x3F) ) {
	return ( -1 );
    }

    for (i = 0; i < loc-1; ++i) {
	if  ( findNext(dirDta) ) {
             return ( -1 );
        }
    }
    return ( 0 );
}
SHAR_EOF
fi # end of overwriting check
if test -f 'dosos.h'
then
	echo shar: will not over-write existing file "'dosos.h'"
else
cat << \SHAR_EOF > 'dosos.h'
/*+
 * Description:
 *	Header file for DOS supplemental facilities not provided
 *	by MSC library.
 *
-*/

/*
 * Author: Mohsen Banan.
 * History:
 * 
 */

/*
 * SCCS Revision: @(#)dosos.h	1.1    Released: 8/3/87
 */

#ifndef _DOSOS_H_	/*{*/
#define _DOSOS_H_

#define NAMESIZ	13	/* 12 character name + '\0' */

/* DOS Disk Transfer Address table */
typedef struct DirDta {
    char reserved[21];	       	        /* used in "find next" operation */
    char attr;				/* file attribute byte */
    unsigned short ftime;		/* time of last modification */
    unsigned short fdate;		/* date of last modification */
    unsigned long fsize;		/* file size in bytes */
    char fname[NAMESIZ];	        /* filename and extension */
} DirDta;

#ifdef M_I86SM 
#define GET_OFF(ptr)   ((unsigned) ptr)
static unsigned _showds() { struct SREGS r; segread(&r); return r.ds;}
#define GET_SEG(ptr)   _showds();
#endif

#endif	/*}*/
SHAR_EOF
fi # end of overwriting check
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
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
fi # end of overwriting check
if test -f 'getopt.c'
then
	echo shar: will not over-write existing file "'getopt.c'"
else
cat << \SHAR_EOF > 'getopt.c'
/*
 * getopt - get option letter from argv
 */

#include <stdio.h>
#include <string.h>

char	*optarg;	/* Global argument pointer. */
int	optind = 0;	/* Global argv index. */

static char	*scan = NULL;	/* Private scan pointer. */


int
getopt(argc, argv, optstring)
int argc;
char *argv[];
char *optstring;
{
	register char c;
	register char *place;

	optarg = NULL;

	if (scan == NULL || *scan == '\0') {
		if (optind == 0)
			optind++;
	
		if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
			return(EOF);
		if (strcmp(argv[optind], "--")==0) {
			optind++;
			return(EOF);
		}
	
		scan = argv[optind]+1;
		optind++;
	}

	c = *scan++;
	place = strchr(optstring, c);

	if (place == NULL || c == ':') {
		fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
		return('?');
	}

	place++;
	if (*place == ':') {
		if (*scan != '\0') {
			optarg = scan;
			scan = NULL;
		} else if (optind < argc) {
			optarg = argv[optind];
			optind++;
		} else {
			fprintf(stderr, "%s: -%c argument missing\n", argv[0], c);
			return('?');
		}
	}

	return(c);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'makefile'
then
	echo shar: will not over-write existing file "'makefile'"
else
cat << \SHAR_EOF > 'makefile'
#
#  Description: 
#
#
# SCCS Revision: @(#)makefile	1.1    Released: 8/3/87
#
# :::::::::::::

PKG= $(LIBS_PATH)/dosos.a
PUB_H= 
LCL_H= 
LCL_C= dosdir.c bsddir.c 
C_SRC=  $(PUB_H) $(LCL_H) $(LCL_C)
#
OBJS= dosdir.o bsddir.o 

#
$(PKG):       $(OBJS)
	${LB} ${PKG} ${OBJS}
	${RANLIB} ${PKG}
#
e_pub_h:
	@echo ${PUB_H}
e_lcl_h:
	@echo ${LCL_H}
e_lcl_c:
	@echo ${LCL_C}
e_c_src:
	@echo ${C_SRC}
e_objs:
	@echo ${OBJS}
e_pkg:
	@echo ${PKG}
#
ctags:  ${LCL_C}
	ctags $(LCL_C)
SHAR_EOF
fi # end of overwriting check
if test -f 'pcmake'
then
	echo shar: will not over-write existing file "'pcmake'"
else
cat << \SHAR_EOF > 'pcmake'
#
#  Description: 
#
#
# SCCS Revision: @(#)pcmake	1.1    Released: 8/3/87
#
# :::::::::::::

CC= msc
#CFLAGS=-DMSDOS -Zd -Od
CFLAGS=-DMSDOS  -I..\\include 

AS= masm
AFLAGS= /mX

LIBRARIAN= lib
LIBFLAGS= 

.c.obj:
	$(CC) $(CFLAGS)  $*.c;

.asm.obj:
	$(AS) $(AFLAGS) $*.asm;
 
PKG= dossup
PUB_H= 
LCL_H= 
C_1= dosdir.c bsddir.c getopt.c traverse.c
LCL_C= $(C_1) 
C_SRC=  $(PUB_H) $(LCL_H) $(LCL_C)
#
O_1= dosdir.obj bsddir.obj getopt.obj traverse.obj
OBJS= $(O_1) 

#
$(PKG).lib:       $(OBJS) 
	del $(PKG).lib
	$(LIBRARIAN) $(PKG) $(LIBFLAGS) $(O_1);

LINKER= link
LNKFLAGS= /STACK:16384
ARGV= \\msc\\lib\\ssetargv.obj
PDT= traverse

#
e_make:
	@echo $(MAKEFILE)
e_pub_h:
	@echo $(PUB_H)
e_lcl_h:
	@echo $(LCL_H)
e_lcl_c:
	@echo $(LCL_C)
e_c_src:
	@echo $(C_SRC)
e_objs:
	@echo $(OBJS)
e_libs:
	@echo $(LIBS)
e_pdt:
	@echo $(PDT)
#
tags:  $(LCL_C)
		ctags $(LCL_C)
SHAR_EOF
fi # end of overwriting check
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 # end of overwriting check
if test -f 'traverse.c'
then
	echo shar: will not over-write existing file "'traverse.c'"
else
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 cwd[80];
	char	*root = argc > 1 ? argv[1] : ".";
        getcwd(cwd, sizeof(cwd));
	traverse (root, tryverse);
        chdir(cwd);
        printf("cwd=%s\n", cwd);
	}
#endif
SHAR_EOF
fi # end of overwriting check
cd ..
if test ! -d 'lsr'
then
	mkdir 'lsr'
fi
cd 'lsr'
if test -f 'build.bat'
then
	echo shar: will not over-write existing file "'build.bat'"
else
cat << \SHAR_EOF > 'build.bat'
 msc -DMSDOS -I. -I..\include   .\lsr.c;
 link  /STACK:8000  lsr.obj   \msc\lib\ssetargv.obj , lsr,,  dossup  ;
SHAR_EOF
fi # end of overwriting check
if test -f 'env'
then
	echo shar: will not over-write existing file "'env'"
else
cat << \SHAR_EOF > 'env'
set INCFLAGS = "-I. -I../include"
SHAR_EOF
fi # end of overwriting check
if test -f 'lsr.c'
then
	echo shar: will not over-write existing file "'lsr.c'"
else
cat << \SHAR_EOF > 'lsr.c'
/*+
 * Description:
 *	Recursevely list all files within a set of directories.
 *	This utility can be used as a front end to other tools 
 *	that do don't provide for traversing of directories.
 * 	"lsr . | makekit " is an example of a possible usage.
 *	USAGE:
 *		lsr [-f] fileName
 *	-f:	Do not list directory names. Files only.
-*/

/*
 * Author: Mohsen Banan.
 * History:
 *
 */

#ifdef SCCS_VER	/*{*/
static char sccs[] = "@(#)lsr.c	1.1    Released: 8/3/87";
#endif /*}*/

/* #includes */
#include  <stdio.h>
#include  "estd.h"
#include <ctype.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef MSDOS
#include <direct.h>
#endif

 
STATIC Void usage();
STATIC Void tryverse();

STATIC  Char * progName;
static char rootName[256] = { 0 };
static Bool filesOnlyFlag = FALSE;

#ifdef MSDOS
static char cwd[128];
#endif


/*<
 * Function:
 * Description:
 *
 * Arguments:
 *
 * Returns:
 *
 * 
>*/
Int
main (argc, argv)
Int argc;
Char * argv[];
{
    Bool errFlag = FALSE;
    Bool usageFlag = FALSE;
    extern char * optarg;
    extern int optind;
    Int c;

    progName = argv[0];

    while ((c = getopt(argc, argv, "fFuU")) != EOF) {
        switch (c) {
	case 'f':
	case 'F':
	     filesOnlyFlag = TRUE;
	     break;
	case 'u':
	case 'U':
	    usageFlag = TRUE;
	    break;
        case '?':
        default:
            errFlag = TRUE;
            break;
        } 
    }

    if ( usageFlag ) {
	usage();
	exit(0);
    }
    if ( errFlag ) {
        usage();
        exit(1);
    }

#ifdef MSDOS
    if (getcwd(cwd, sizeof(cwd)) == (char *) 0) {
	perror("getcwd");
	exit(1);
    }
#endif

    do {
	char *path;
	struct	stat	buf;
#define	isdir(path)  stat(path, &buf) ? 0 : (buf.st_mode&S_IFMT)==S_IFDIR)

	if ( optind >= argc ) {
	    path = ".";
	} else {
	    path = argv[optind];
	}
	if ( isdir(path) {
	    rootName[0] = '\0';
	    traverse(path, tryverse);
	} else {
	    printf("%s\n", path);
	}
    } while ( ++optind < argc );

#ifdef MSDOS
    if (chdir(cwd)) {
	perror("chdir");
        exit(1);
    }
#endif

    exit (0);
}


STATIC Void
usage ()
{
    fprintf(stderr, "Usage: %s [-f] [filename] ...\n", progName);
}


/*<
 * Function:
 * Description:
 *
 * Arguments:
 *
 * Returns:
 *
>*/
static Void
tryverse (file, type, pos)
char	*file;
{
	int 	in;
	if (pos == 0) {
		if (type == 'd') {
		    if ( * rootName ) {
			sprintf(rootName,"%s/%s", rootName, file);
		    } else {
			sprintf(rootName,"%s", file);
		    }
		    if ( filesOnlyFlag == FALSE ) {
		        printf ("%s\n", rootName);
  		    }
		} else {
		    printf ("%s/%s\n", rootName, file);
		}
	} else {
	    if (type == 'd') {
		extern char *strrchr();
		char *rootEnd;

		rootEnd = strrchr(rootName, '/');
		if (  rootEnd ) {
		    *rootEnd = '\0';
		}
	    }
	}
}

SHAR_EOF
fi # end of overwriting check
if test -f 'makefile'
then
	echo shar: will not over-write existing file "'makefile'"
else
cat << \SHAR_EOF > 'makefile'
#
#  Description: 
#
#
# SCCS Revision: @(#)makefile	1.1    Released: 8/3/87
#
# :::::::::::::

PDT = lsr
PUB_H =
LCL_H =
LCL_C = lsr.c
C_SRC =  ${PUB_H} ${LCL_H} ${LCL_C}
OBJS = lsr.o
#
PUBSUP = $(LIBS_PATH)/pubsup.a
LIBS = $(PUBSUP)

#
$(PDT):       $(OBJS) ${LIBS}
	${LK} -o ${PDT} ${OBJS} ${LIBS} 
#
e_pub_h:
	@echo ${PUB_H}
e_lcl_h:
	@echo ${LCL_H}
e_lcl_c:
	@echo ${LCL_C}
e_c_src:
	@echo ${C_SRC}
e_objs:
	@echo ${OBJS}
e_libs:
	@echo ${LIBS}
e_pdt:
	@echo ${PDT}
#
tags:  ${LCL_C}
	ctags $(LCL_C)

SHAR_EOF
fi # end of overwriting check
if test -f 'pcmake'
then
	echo shar: will not over-write existing file "'pcmake'"
else
cat << \SHAR_EOF > 'pcmake'
#
#  Description: 
#
#
# SCCS Revision: @(#)pcmake	1.1    Released: 8/3/87
#
# :::::::::::::
CC= msc
#CFLAGS=-DMSDOS  -Zi -Od
GLOB=..
CFLAGS=-DMSDOS -I. -I$(GLOB)\\include

LINKER= link
LINKFLAGS= /STACK:8000
#LINKFLAGS= 

LIBRARIAN= lib
LIBFLAGS= 
HERE= .

.c.obj:
	$(CC) $(CFLAGS)  $(HERE)\\$*.c;

PDT= lsr
PKG=
PUB_H=
LCL_H= 
LCL_C= lsr.c 
C_SRC=  $(PUB_H) $(LCL_H) $(LCL_C)
O_1=
OBJS= lsr.obj 
#
ARGV= \\msc\\lib\\ssetargv.obj
#

DOSSUP= dossup
LIBS= $(DOSSUP) 

#

$(PDT).exe:	$(OBJS)
	$(LINKER) $(LINKFLAGS) $(OBJS) $(ARGV) ,$(PDT),,$(LIBS) ;


$(PKG).lib:       $(O_1) 
	del $(PKG).lib
	$(LIBRARIAN) $(PKG) $(LIBFLAGS) $(O_1);

#
e_make:
	@echo $(MAKEFILE)
e_pub_h:
	@echo $(PUB_H)
e_lcl_h:
	@echo $(LCL_H)
e_lcl_c:
	@echo $(LCL_C)
e_c_src:
	@echo $(C_SRC)
e_objs:
	@echo $(OBJS)
e_libs:
	@echo $(LIBS)
e_pdt:
	@echo $(PDT)
#
ctags:  $(LCL_C)
		$(CTAGS) $(LCL_C)

pr: $(C_SRC)
	mbcpr -r -n $(C_SRC)
SHAR_EOF
fi # end of overwriting check
cd ..
if test ! -d 'shar'
then
	mkdir 'shar'
fi
cd 'shar'
if test -f 'build.bat'
then
	echo shar: will not over-write existing file "'build.bat'"
else
cat << \SHAR_EOF > 'build.bat'
 msc -DMSDOS  -I\d\pub\include   shar.c;
 link  /STACK:16384  shar.obj   \msc\lib\ssetargv.obj , shar,,  dossup ;
SHAR_EOF
fi # end of overwriting check
if test -f 'makefile'
then
	echo shar: will not over-write existing file "'makefile'"
else
cat << \SHAR_EOF > 'makefile'
#
#  Description: 
#
#
# SCCS Revision: %W%    Released: %G%
#
# :::::::::::::

PDT = shar
PUB_H =
LCL_H =
LCL_C = shar.c
C_SRC =  ${PUB_H} ${LCL_H} ${LCL_C}
OBJS = shar.o
#
PUBSUP = $(LIBS_PATH)/pubsup.a
LIBS = $(PUBSUP)

#
$(PDT):       $(OBJS) ${LIBS}
	${LK} -o ${PDT} ${OBJS} ${LIBS} 
#
e_pub_h:
	@echo ${PUB_H}
e_lcl_h:
	@echo ${LCL_H}
e_lcl_c:
	@echo ${LCL_C}
e_c_src:
	@echo ${C_SRC}
e_objs:
	@echo ${OBJS}
e_libs:
	@echo ${LIBS}
e_pdt:
	@echo ${PDT}
#
tags:  ${LCL_C}
	ctags $(LCL_C)

SHAR_EOF
fi # end of overwriting check
if test -f 'pcmake'
then
	echo shar: will not over-write existing file "'pcmake'"
else
cat << \SHAR_EOF > 'pcmake'
# ::::::::::::::::::::::::::::::::::::::::::::::::::
#
#  File: Makefile
#  Description: Makefile for creating 
#
#
#  Audit Trail:  $Log:$
#
#  $Header: $
#
# ::::::::::::::::::::::::::::::::::::::::::::::::::

CC= msc
#CFLAGS=-DMSDOS -Zd -Od
CFLAGS=-DMSDOS  -I\\d\\pub\\include 

LINKER= link
LNKFLAGS= /STACK:16384
#LNKFLAGS= 

.c.obj:
	$(CC) $(CFLAGS)  $*.c;

MAKEFILE= makefile
PDT= shar
PUB_H= 
LCL_H=
LCL_C= shar.c 
C_SRC=  $(PUB_H) $(LCL_H) $(LCL_C)
OBJS= shar.obj 
ARGV= \\msc\\lib\\ssetargv.obj

#

DOSSUP= dossup
LIBS= $(DOSSUP)

#

$(PDT).exe:       $(OBJS) 
	$(LINKER) $(LNKFLAGS) $(OBJS) $(ARGV) ,$(PDT),,$(LIBS) ;

#
e_make:
	@echo $(MAKEFILE)
e_pub_h:
	@echo $(PUB_H)
e_lcl_h:
	@echo $(LCL_H)
e_lcl_c:
	@echo $(LCL_C)
e_c_src:
	@echo $(C_SRC)
e_objs:
	@echo $(OBJS)
e_libs:
	@echo $(LIBS)
e_pdt:
	@echo $(PDT)
#
tags:  $(LCL_C)
		ctags $(LCL_C)
SHAR_EOF
fi # end of overwriting check
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 "June 3, 1985" "UNIX User's Manual" "Wang Institute"
.SH NAME
shar \- create file storage archive for extraction by /bin/sh
.SH SYNOPSIS
.B shar
[-abcsv] [-d delim] [-p prefix] files
.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 them 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 emit commands to make executable plain files executable.
.I shar will not allow existing files to be over-written;
such files must be removed by the file extractor.
.SH OPTIONS
.de OP
.TP
.B -\\$1
..
.OP a
All the options.
The options:
.B "-v -c -b -p <tab>X"
are implied.
.OP s
Silent running.
All checking and extra output is inhibited.
.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.
.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 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 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.
.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.
.SH "SEE ALSO
tar(1), cpio(1), tp(1), sh(1)
.SH AUTHOR
Gary Perlman
(based on a shell version by James Gosling,
with additions motivated by
Derek Zahn,
Michael Thompson,
H. Morrow Long,
Fred Avolio,
Gran Uddeborg,
&
Chuck Wegrzyn)
.SH LIMITATIONS
.I shar
does not know anything about
links between files
or binary files.
SHAR_EOF
fi # end of overwriting check
if test -f 'shar.c'
then
	echo shar: will not over-write existing file "'shar.c'"
else
cat << \SHAR_EOF > 'shar.c'
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#ifdef MSDOS
#include <direct.h>
#endif

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

/* COMMANDS */
#define	EXTRACT "#! /bin/sh"     /* magic exec string at shar file start */
#define	PATH    "/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 +x"       /* change file protection (for executables) */
#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 */

main (argc, argv) char **argv;	
	{
	char cwd[80];
	int 	shar ();
	int 	optind;

	if ((optind = initial (argc, argv)) < 0)
		exit (1);
	if (header (argc, argv, optind))
		exit (2);
#ifdef MSDOS
        if ( getcwd(cwd, sizeof(cwd)) == (char *)0 ) 
  		{
		perror("getcwd error");
                exit(3);
                }
#endif
	while (optind < argc)
		traverse (argv[optind++], shar);
#ifdef MSDOS
        if (chdir(cwd)) 
  		{
		perror("chdir error");
                exit(4);
                }
#endif
	footer ();
	exit (0);
	}

/*			OPTIONS			*/
typedef	int	lgl;
#define	true	((lgl) 1)
#define	false	((lgl) 0)
int 	Lastchar;   /* the last character printed */
int 	Ctrlcount;  /* how many bad control characters are in file */

#define	USAGE "[-abcsv] [-p prefix] [-d delim] files > archive"
#define	OPTSTRING "abcsvp:d:"

lgl 	Verbose = false;       /* provide append/extract feedback */
lgl 	Basename = false;      /* extract into basenames */
lgl 	Count = false;         /* count characters to check transfer */
lgl 	Silent = false;        /* turn off all verbosity */
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 */

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;
	while ((C = getopt (argc, argv, OPTSTRING)) != EOF)
		switch (C)
			{
			case 'v': Verbose = true; break;
			case 'c': Count = true; break;
			case 'b': Basename = true; break;
			case 'd': Delim = optarg; break;
			case 's': /* silent running */
				Silent = true;
				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";
			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);
		return (-1);
		}
	return (optind);
	}

header (argc, argv, optind)
char	**argv;
	{
	int 	i;
	lgl 	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 (problems);
	/*	I have given up on putting a cut line in the archive.
		Too many people complained about having to remove it.
		puts ("-----cut here-----cut here-----cut here-----cut here-----");
	*/
	puts (EXTRACT);
	puts ("# This is a shell archive, meaning:");
	printf ("# 1. Remove everything above the %s line.\n", EXTRACT);
	puts ("# 2. Save the resulting text in a file.");
	puts ("# 3. Execute the file with /bin/sh (not csh) to create the files:");
	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 (0);
	}

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

archive (input, output)
char	*input, *output;
	{
	char	buf[BUFSIZ];
	FILE	*ioptr;
	if (ioptr = fopen (input, "r"))
		{
		if (Count == true)
			{
			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);
		/* thanks to H. Morrow Long (ittvax!long) for the next fix */
		if (Lastchar != '\n') /* incomplete last line */
			putchar ('\n');   /* Delim MUST begin new line! */
		puts (Delim);
		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 (0);
		}
	else
		{
		fprintf (stderr, "shar: Can't open '%s'\n", input);
		return (1);
		}
	}

/*
	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;
	}

outline (s)
register	char	*s;
	{
	if (*s)
		{
		while (*s)
			{
			if (Count == true && badctrl (*s)) Ctrlcount++;
			putchar (*s++);
			}
		Lastchar = *(s-1);
		}
	}

#define	FSIZE     statbuf.st_size
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;
	if (!strcmp (file, ".")) return;
	if (stat (file, &statbuf)) FSIZE = 0;
	if (Basename == true)
		{
		while (*basefile) basefile++; /* go to end of name */
		while (basefile > file && *(basefile-1) != '/') basefile--;
		}
	if (pos == 0) /* before the file starts */
		{
		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, FSIZE, FSIZE > 1 ? "s" : "");
			if (Silent == false) /* this solution by G|ran Uddeborg */
				{
				printf ("if %s -f '%s'\n", TEST, basefile);
				puts ("then");
				printf ("	%s: will not over-write existing file \"'%s'\"\n",
					ECHO, basefile);
				puts ("else");
				}
			if (archive (file, basefile)) exit (-1);
			}
		}
	else /* pos == 1, after the file is archived */
		{
		if (type == 'd')
			{
			if (Verbose == true)
				printf ("%s: done with directory \"'%s'\"\n", ECHO, basefile);
			printf ("%s ..\n", CHDIR);
			}
		else /* type == 'f' (plain file) */
			{
			if (Count == true)
				{
				printf ("if %s %d -ne \"`%s '%s'`\"\n",
					TEST, FSIZE, WC_C, basefile);
				puts ("then");
				printf ("	%s: error transmitting \"'%s'\" ", ECHO, basefile);
				printf ("'(should have been %d character%s)'\n",
					FSIZE, FSIZE > 1 ? "s" : "");
				puts ("fi");
				}
#ifndef MSDOS
			if (access (file, 1) == 0) /* executable -> chmod +x */
				printf ("%s '%s'\n", CHMOD, basefile);
#endif
			if (Silent == false)
				{
				puts ("fi # end of overwriting check");
				}
			}
		}
	}
SHAR_EOF
fi # end of overwriting check
if test -f 'env'
then
	echo shar: will not over-write existing file "'env'"
else
cat << \SHAR_EOF > 'env'
set INCFLAGS = "-I. -I../include"
SHAR_EOF
fi # end of overwriting check
cd ..
#	End of shell archive
exit 0