[net.sources] A batch system for 4.2

shimell@stc.UUCP (Dave Shimell) (04/27/85)

<eat me>
	Here is a batch system for BSD4.2 written by Bruce Ollie Munro.
	(I am posting it as he is currently away on a course - please
	address flames etc to ollie@stc.UUCP)

	The batch system uses the line printer queueing system to do
	the hard work.  This has the advantages of using standard
	software to control queues.  Thus batch queues may be
	manipulated by lpc.

	We have run this batch system for a number of months with no
	problems.  Please note that the Makefile is an augmented Make
	so this may not work on your system.  To install for the first
	time type:

		make new	(as root)

	May I suggest that if your system is greatly loaded then you
	should think about running the load contol software distributed over
	the net recently from San Diego.  We run it in addition to batch
	and are greatly impressed!
	
Regards,
Dave Shimell.  <shimell@stc.UUCP>
{root44, ukc, idec, stl, creed}!stc!shimell

--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# To unbundle, csh this file
echo Makefile
cat >Makefile <<'End of Makefile'
# Makefile for batch
# %W%
#

USER	=	root

SPOOL	=	/usr/spool/lpd/batch
SCCS	=	.
BIN	=	/usr/local
LIBDIR	=	/usr/local/lib
MAN	=	/usr/man/manl
PROGS	=	batch bback

CFLAGS	=	-O
LIBS	=	-llocal

#-------------------------------------------------------------------------------

all:		$(PROGS)
		@echo programs are up to date

$(PROGS):	$$@.o
		$(CC) $(LDFLAGS) $@.o $(LIBS)
		mv a.out $@

install cp:	$(BIN)/batch $(LIBDIR)/bback $(MAN)/batch.l
		@echo installed programs are up to date

new:		install printcap
		mkdir $(SPOOL)
		cat /dev/null > $(SPOOL)/LOGFILE
		cat printcap >> /etc/printcap

$(BIN)/batch:	batch
		-rm -f $@
		cp $? $@

$(LIBDIR)/bback:bback
		-rm -f $@
		cp $? $@
		chown $(USER) $@
		chmod 4755 $@

$(MAN)/batch.l:	batch.n
		-rm -f $@
		cp $? $@

.DEFAULT:
		$(GET) $(GFLAGS) $(SCCS)/s.$<

rm clean tidy:
		-rm -f $(PROGS) batch.o bback.o batch.n
'End of Makefile'
echo batch.c
cat >batch.c <<'End of batch.c'
#include <stdio.h>
#include <assert.h>

#ifndef lint
#ifndef NSCCS
	static char sccsid[] = "%W%";
#endif
#endif

#define MAXLINE 512

#ifndef TRUE
#	define TRUE  1
#	define FALSE 0
#endif

typedef int bool;

extern char **environ;
extern char *optarg;
extern int  optind;

main(argc, argv)
char **argv;
{
	register char **envp;	/* Environment pointer */
	register char *s1;	/* Pointers used in inserting qoute escape */
	register char *s2;	/* in environment variable names */
	int c;			/* Option character */
	int n;			/* Number of sscanf arguments */
	char name[MAXLINE];	/* Environment variable name  */
	char value[MAXLINE];	/* Environment variable value */
	char value2[MAXLINE];	/* Environment value after escape of quotes */
	char pathname[MAXLINE];	/* Pathname of current working directory */
	char *shell;		/* Name of shell */
	char *ofile = "batch.log";	/* Job output file, set to default */
	char *efile = NULL;	/* Job error file */
	bool notsh;		/* TRUE if shell is other than sh */
	bool  mail = FALSE;	/* Mail required switch */
	FILE *pfp;
	extern FILE *popen();
	extern char *getenv();	/* Routine to access environment variables */
	extern char *getwd();	/* Routine to get current working directory */
	extern int getopt();	/* Routine to return command line options */

	while ((c = getopt(argc, argv, "me:o:")) != EOF) {
		switch (c) {
		case 'o' :
			ofile = optarg;
			break;
		case 'e' :
			efile = optarg;
			break;
		
		case 'm' :
			mail = TRUE;
			break;

		case '?' :
			quit("Usage: batch [-m] [-o file] [-e file] [command [args .... ]]");
		}
	}
	if (efile == NULL)
		efile = ofile;
	if ((pfp = popen("/usr/ucb/lpr -P batch", "w")) == NULL)
		quit("batch: can't pipe to lpr");
	fprintf(pfp, "%s\n", ofile);
	fprintf(pfp, "%s\n", efile);
	fprintf(pfp, "%d\n", mail);
	fprintf(pfp, "%s\n", getwd(pathname));
	fprintf(pfp, "umask %o\n", umask(0));

	/*
	 * Get environment variables for command file,
	 * scanf used to seperate values from names in
	 * order to place quotes around the values so
	 * that those containing spaces will be handled
	 * correctly by 'sh'
	 */

	for (envp = environ; *envp != NULL; envp++) {
		n = sscanf(*envp, "%[^=]=%[^\n]", name, value);
		assert(n == 2);

		/*
		 * Check for occurence of qoute (') in environment values
		 * and replace them with '\'' in order that sh does not
		 * get confused when executing job.
		 */
		 
		s1 = value;
		s2 = value2;
		while (*s2++ = *s1) {	/* Assignment */
			if (*s1++ == '\'') {
				*s2++ = '\\';
				*s2++ = '\'';
				*s2++ = '\'';
			}
		}
		fprintf(pfp, "%s='%s'\n", name, value2);
		fprintf(pfp, "export %s\n", name);
	}

	/*
	 * Checks if shell is not 'sh' and if not places
	 * an invocation line for the correct shell in
	 * the command file
	 */

	shell = getenv("SHELL");
	if (notsh = strcmp(shell, "/bin/sh"))
		fprintf(pfp, "%s << 'xxAARRGGHHxx'\n", shell);
	/* Place user's commands in command file */
	if (argv[optind] == NULL)
		while ((c = getchar()) != EOF)
			putc(c, pfp);
	else {
		for (; argv[optind]; ++optind)
			fprintf(pfp, "%s ", argv[optind]);
		putc('\n', pfp);
	}

	/*
	 * Shell termination line if not 'sh'
	 */

	if (notsh)
		fprintf(pfp, "xxAARRGGHHxx\n");
	pclose(pfp);
	exit(0);
}
'End of batch.c'
echo batch.n
cat >batch.n <<'End of batch.n'
.TH BATCH LOCAL
.SH NAME
batch \- submit a batch command 
.SH SYNTAX
.B batch 
[\-o stdout] [\-e stderr] [\-m] [command [arg ...]]
.SH DESCRIPTION
.I Batch
submits a job to the batch job queue, where it will be run
at low priority. This is useful for large jobs in order
that they do not load the system heavily at peak times.
.B Default
if no command is specified is to read command from the
.I "standard input."
A log of the commands' output is maintained by
.I batch.
.I Batch
interprets only the options, which may be in any order, that
are placed
.I before
the command. The options are:
.TP 10
.B "\-o file"
Appends the standard output of
.I command
to
.I file.
Default file is
.I batch.log.
.TP 10
.B "\-e file"
Appends the standard error of
.I command
to
.I file.
Default is to merge standard error with standard output.
.TP 10
.B \-m
Sends mail to user to inform them of completion of batch job.
.PP
The state of the batch queue may be examined using lpq(1)
invoked as:
.IP
.I "lpq -P batch."
.PP
Batch jobs may be dequeued using lprm(1) invoked as:
.IP
.I "lprm -P batch."
(See RESTRICTIONS below)
.SH EXAMPLES
.sp 3
.IP
.br
batch nroff xyz.n
.PP
will nroff xyz.n placing the standard output in
.I batch.log.
.PP
.IP
batch
.br
nroff xyz.n >xyz.doc
.br
<EOF>
.PP
will nroff xyz.n and redirect the standard output to
.I xyz.doc.
.PP
The same effect could be achieved by:\-
.PP
.IP
batch \-o xyz.doc nroff xyz.n
.PP
The above examples will place any error reports from
the command on the standard output.
.PP
However 
.PP
.IP
batch \-e error.log \-o xyz.doc nroff xyz.n
.PP
nroff's xyz.n appending standard output to xyz.doc
and standard error to error.log.
.s3
.ne 6
.IP
batch \-m
.br
cd ~/mydocs
.br
foreach i (*.n)
.br
	tbl \-TX $i | nroff \-mm | col | lpr
.br
end
.br
<EOF>
.PP
will format documents from the mydocs directory and print them.
When the job is complete mail will be sent to the user.
.SH FILES
.PD 0
.TP 32 
 batch.log
default standard output file
.TP 32
 /usr/spool/lpd/batch/LOGFILE
record of all batch jobs and their time statistics
.PD
.SH "SEE ALSO"
lpq(1), lprm(1), lpc(8), lpd(8)
.SH RESTRICTIONS
If the command to be batched is specified on the invocation line
all shell metacharacters should be escaped in order that they are
not interpreted by the current login shell.
.PP
Using lprm(1) on the active batch job will cause some error messages.
If the job removed is the only one in the queue then no harm is done,
however if there are jobs waiting removing the active job will cause
the daemon to stop and the queue must be restarted by the system administrator.
'End of batch.n'
echo bback.c
cat >bback.c <<'End of bback.c'
#include <stdio.h>
#include <pwd.h>
#include <assert.h>
#include <sys/param.h>
#include <sys/quota.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>

#ifndef NSCCS
#ifndef lint
	static char sccsid[] = "%W%";
#endif
#endif

typedef int bool;

#define MAXLINE 512
#define DAEMON_UID 1
#define LOW_PRIORITY 10

#define NORM_EXIT 0
#define BAD_EXIT  2

/*
 * convert whole seconds to number of clock
 * cycles by multiplying by CYCLE_SECS
 */

#define  CYCLE_SECS  100

/*
 * convert micro-seconds to number of clock
 * cycles by dividing by USEC_CYCLES
 */

#define  USEC_CYCLES 10000

/*
 * reduce number in micro-secs to tenths
 * secs by dividing by RED_TO_TENTHS
 */

#define  RED_TO_TENTHS  100000
#define  SECS_IN_MIN  60

extern char *optarg;
extern char *program;
extern int  fail_status;
char *user;			/* User's login name */

extern char *gets();

main(argc, argv)
char **argv;
{

	char ofile[MAXLINE];		/* STDOUT filename */
	char efile[MAXLINE];		/* STDERR filename */
	char dir_path[MAXLINE];
	int c;
	union wait status;		/* Exit status of child  */
	union wait execute();

	int n;
	bool mail;			/* Mail required switch */
	struct passwd *pw;		/* structure for user's passwd file entry */

	program = "batch backend";
	fail_status = BAD_EXIT;
	while ((c = getopt(argc, argv, "w:l:i:n:h:")) != EOF) {
		switch (c) {

		case 'n':
			user = optarg;
			break;

		case '?':
			quit("Bad option -%c from lpd", c);

		}
	}

	/*
	 * Check for attempts to use backend as root
	 * or daemon  by bypassing  queueing process
	 */

	if (getuid() != DAEMON_UID) {
		pw = getpwuid(geteuid());
		quit("attempt by user %s to use status of %s in batch", pw->pw_name, user);
	}

	/*
	 * Get user's uid and gid from passwd file.
	 */

	if ((pw = getpwnam(user)) == NULL)
		quit("Non-existent user name- %s from lpd", user);
	endpwent();

	/*
	 * Set unbuffered input so that parent doesn't
	 * read any command lines intended for child's use
	 */

	setbuf(stdin, NULL);
	gets(ofile);
	gets(efile);
	n = scanf("%d\n", &mail);
	assert(n == 1);

	/*
	 * Change to passed directory name
	 */

	if (chdir(gets(dir_path)) == -1)
		quit("Couldn't access directory (%s)", dir_path);
	status = execute(ofile, efile, pw->pw_uid, pw->pw_gid);
	if (status.w_T.w_Termsig || status.w_T.w_Retcode)
		badstat(status);
	if (mail)
		sendmail(user, status);
	exit(NORM_EXIT);
}

/*
 * forks,  execs sh  with output  directed
 * to ofile and efile and does  setuid and
 * setgid to specified values
 */

union wait
execute(ofile, efile, uid, gid)
char *ofile;
char *efile;
int  uid;
int  gid;
{
	int pid;		/* Return value from fork system call */
	union wait status;	/* Return value from time_command function */
	union wait time_job();

	if ((pid = fork()) == -1)
		quit("couldn't fork");
	if (pid == 0) {
		if (quota(Q_SETUID, uid, NULL, NULL) == -1)
			quit("Cannot set quotas for %s", user);
		setgid(gid);
		setuid(uid);
		if (freopen(ofile, "a", stdout) != stdout)
			quit("can't create %s", ofile);
		if (strcmp(ofile, efile) == 0) {
			if (dup2(1, 2) == -1)
				quit("can't duplicate standard error to standard output");
		} else if (freopen(efile, "a", stderr) != stderr)
			quit("can't create %s", efile);
		setbuf(stdout, NULL);
		setpriority(PRIO_PROCESS, getpid(), LOW_PRIORITY);
		execl("/bin/sh", "sh", "-s", NULL);
		quit("execl failed");
	}
	status = time_job();
	return(status);
}

/*
 * Waits for job to finish, collects resource stats on job
 * from wait3 system call and outputs on stderr a summary
 * of these stats in similar format to the csh time command
 */

union wait
time_job()
{
	union wait status;	/* Return value from wait3 system call */
	struct rusage rusage;	/* Resources used by process */
	struct timeval inittime;/* Time returned by gettimeofday system call */
	struct timeval endtime;	/* ditto */
	struct timezone dummy;	/* time zone correction (not used) */
	u_long elap_time;	/* Elapsed time during execution of job */
	long cpu_cycles;	/* No. of cycles of cpu time */
	long elap_cycles;	/* Elapsed time in terms of cycles */

	gettimeofday(&inittime, &dummy);
	wait3(&status, NULL, &rusage);
	gettimeofday(&endtime, &dummy);
	elap_time = endtime.tv_sec - inittime.tv_sec;
	elap_cycles = ((endtime.tv_sec * CYCLE_SECS) + (endtime.tv_usec/USEC_CYCLES)) -
		((inittime.tv_sec * CYCLE_SECS) + (inittime.tv_usec/USEC_CYCLES));
	cpu_cycles = (((rusage.ru_utime.tv_sec + rusage.ru_stime.tv_sec) * CYCLE_SECS) +
		((rusage.ru_utime.tv_usec + rusage.ru_stime.tv_usec)/USEC_CYCLES));

	fprintf(stderr, "Job: %-9s", user);
	fprintf(stderr, "%ld.%ldu %ld.%lds %ld:%02ld %02ld%% %ld+%ldk %ld+%ldio %ldpf+%ldw\n",
		rusage.ru_utime.tv_sec,
		rusage.ru_utime.tv_usec/RED_TO_TENTHS,
		rusage.ru_stime.tv_sec,
		rusage.ru_stime.tv_usec/RED_TO_TENTHS,
		elap_time/SECS_IN_MIN, elap_time % SECS_IN_MIN,
		cpu_cycles * CYCLE_SECS/elap_cycles,
		rusage.ru_ixrss/cpu_cycles,
		(rusage.ru_idrss + rusage.ru_isrss)/cpu_cycles,
		rusage.ru_inblock,
		rusage.ru_oublock,
		rusage.ru_majflt,
		rusage.ru_nswap
	);
	return(status);
}

/*
 * Sends mail to the user on completion of the job,
 * indicating the exit status of the job
 */
 
sendmail(user, status)
char *user;
union wait status;
{
	FILE *pp;
	char mail[MAXLINE];
	extern FILE *popen();

	sprintf(mail, "/usr/ucb/mail -s 'batch job' %s", user);
	if ((pp = popen(mail, "w")) == NULL)
		quit("can't pipe to /usr/ucb/mail");
	fprintf(pp, "Your batch job ");
	if (!status.w_T.w_Termsig && !status.w_T.w_Retcode)
		fprintf(pp, "completed successfully.\n");
	else {
		fprintf(pp, "terminated abnormally ");
		if (status.w_T.w_Termsig == 0)
			fprintf(pp, "with exit status %d.\n", status.w_T.w_Retcode);
		else {
			fprintf(pp, "due to signal %d.\n", status.w_T.w_Termsig);
			if (status.w_T.w_Coredump != 0)
				fprintf(pp, "Signal caused a core dump.\n");
		}
	}
	pclose(pp);
}

badstat(status)
union wait status;
{
	if (status.w_T.w_Termsig == 0)
		error("job exited with status %u", status.w_T.w_Retcode);
	else {
		error("job terminated due to signal %u", status.w_T.w_Termsig);
		if (status.w_T.w_Coredump)
			error("Core dumped");
	}
}
'End of bback.c'
echo printcap
cat >printcap <<'End of printcap'
batch|batch job queue:\
	:lp=/dev/null:sd=/usr/spool/lpd/batch:\
	:if=/usr/local/lib/bback:\
	:lf=/usr/spool/lpd/batch/LOGFILE:
'End of printcap'
-- 

Regards,
Dave Shimell.  <shimell@stc.UUCP>
{root44, ukc, idec, stl, creed}!stc!shimell

shimell@stc.UUCP (Dave Shimell) (04/27/85)

<I'm hungry>

	Here are the routines required for the local library.  These
	routines are required for the batch program in the previous
	posting.  Note, these routines were originally built for
	portability + convenience.

	The Makefile will build a library for BSD4.2 by default.

Regards,
Dave Shimell.  <shimell@stc.UUCP>
{root44, ukc, idec, stl, creed}!stc!shimell

-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=--=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
# To unbundle, csh this file
echo Makefile
cat >Makefile <<'End of Makefile'
# Makefile for local libraries
# %W%
#

SCCS	=	.
BIN	=	/usr/local/lib

LIB	=	liblocal.a
CFLAGS	=	-O

#-------------------------------------------------------------------------------

all:		$(LIB)
		@echo programs are up to date

.DEFAULT:
		$(GET) $(GFLAGS) $(SCCS)/s.$<

rm clean tidy:

cp install:	$(LIB)
		-rm -f $(BIN)/$(LIB)
		cp $(LIB) $(BIN)/$(LIB)
		ranlib $(BIN)/$(LIB)

# The following is a list of files for BDS4.2.

$(LIB):		\
		$(LIB)(curdir.o)\
		$(LIB)(dname.o)\
		$(LIB)(fswitch.o)\
		$(LIB)(getopt.o)\
		$(LIB)(sname.o)\
		$(LIB)(error.o)\
		$(LIB)(quit.o)\
		$(LIB)(warning.o)\
		$(LIB)(salloc.o)\
		$(LIB)(ename.o)

# The following is a complete list of files.
# This will need editing for your system and
# moved above.

#$(LIB):		
#
#		$(LIB)(curdir.o)\
#		$(LIB)(dname.o)\
#		$(LIB)(fswitch.o)\
#		$(LIB)(getopt.o)\
#		$(LIB)(rename.o)\
#		$(LIB)(sname.o)\
#		$(LIB)(umask.o)\
#		$(LIB)(ndir.h)\
#		$(LIB)(opendir.o)\
#		$(LIB)(closedir.o)\
#		$(LIB)(readdir.o)\
#		$(LIB)(seekdir.o)\
#		$(LIB)(telldir.o)\
#		\
#		$(LIB)(error.o)\
#		$(LIB)(quit.o)\
#		$(LIB)(warning.o)\
#		$(LIB)(salloc.o)\
#		$(LIB)(ename.o)\
'End of Makefile'
echo closedir.c
cat >closedir.c <<'End of closedir.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <sys/types.h>
#include <ndir.h>

/*
 * close a directory.
 */
void
closedir(dirp)
	register DIR *dirp;
{
	close(dirp->dd_fd);
	dirp->dd_fd = -1;
	dirp->dd_loc = 0;
	free(dirp);
}
'End of closedir.c'
echo curdir.c
cat >curdir.c <<'End of curdir.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <stdio.h>

extern FILE *popen();

curdir(pwd)
char *pwd;		/* Fill this in with the current directory */
{
	register FILE *fp;
	int reply = -1;

	if ((fp = popen("pwd", "r")) != NULL) {
		reply = 0;
		fscanf(fp, " %s ", pwd);
		pclose(fp);
	}
	return(reply);
}
'End of curdir.c'
echo dname.c
cat >dname.c <<'End of dname.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

char *
dname(nam)
char *nam;
{
	register char *slash;
	register char *cp;

	/*
	 * Find last slash in nam.
	 */

	for (slash = cp = nam; *cp != '\0'; cp++)
		if (*cp == '/')
			slash = cp;

	if (slash == nam)
		nam = (*slash == '/' ? "/" : ".");
	else
		*slash = '\0';
	return(nam);
}
'End of dname.c'
echo ename.c
cat >ename.c <<'End of ename.c'
#include <stdio.h>

extern char *sname();

#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

/*
 * Returns a pointer to the file type (file
 * extension) or NULL if one cannot be found.
 * Eg.
 * /usr.xyz/file.plm	returns ".plm"
 * /usr.xyz/file	returns NULL
 */

char *
ename(file)
register char *file;
{
	register char *dot = NULL;

	for (file = sname(file); *file; file++)
		if (*file == '.')
			dot = file;
	return(dot);
}
'End of ename.c'
echo error.c
cat >error.c <<'End of error.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <stdio.h>

extern char *program;

unsigned ecount;

/* VARARGS1 */
error(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
char *s;
{
	ecount++;
	fflush(stdout);
	fprintf(stderr, "%s: error, ", program);
	fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
	fprintf(stderr, "\n");
}
'End of error.c'
echo fswitch.c
cat >fswitch.c <<'End of fswitch.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <stdio.h>
#include <assert.h>

#ifndef TRUE
#	define TRUE 1
#	define FALSE 0
#endif

fswitch()
{
	assert(FALSE);
}
'End of fswitch.c'
echo getopt.c
cat >getopt.c <<'End of getopt.c'
#include <stdio.h>

#define ERR(S, A)	if (opterr) error((S), (A)); else

int	opterr = 1;
int	optind = 1;
int	optopt;
char	*optarg;
char	*program;

extern	char *sname();
extern	char *index();

#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

int
getopt(argc, argv, opts)
char **argv, *opts;
{
	static int sp = 1;
	register c;
	register char *cp;

	if (program == NULL)
		program = sname(argv[0]);

	if (sp == 1) {
		if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
			return(EOF);
		else if (strcmp(argv[optind], "--") == NULL) {
			optind++;
			return(EOF);
		}
	}
	optopt = c = argv[optind][sp];
	if (c == ':' || (cp = index(opts, c)) == NULL) {
		ERR("illegal option -%c", c);
		if (argv[optind][++sp] == '\0') {
			optind++;
			sp = 1;
		}
		return('?');
	}
	if (*++cp == ':') {
		if (argv[optind][sp + 1] != '\0')
			optarg = &argv[optind++][sp + 1];
		else if (++optind >= argc) {
			ERR("option -%c requires an argument", c);
			sp = 1;
			return('?');
		} else
			optarg = argv[optind++];
		sp = 1;
	} else {
		if (argv[optind][++sp] == '\0') {
			sp = 1;
			optind++;
		}
		optarg = NULL;
	}
	return(c);
}
'End of getopt.c'
echo ndir.h
cat >ndir.h <<'End of ndir.h'
static char ndir[] = "%W%";

/*
 * Change notice (rti!trt):  To be compatible with non-bsd systems:
 * #defines for u_short, u_long, and void added.
 */

#define	u_short	unsigned short
#define	u_long	long
#define	void

/*
 * A directory consists of some number of blocks of DIRBLKSIZ
 * bytes, where DIRBLKSIZ is chosen such that it can be transferred
 * to disk in a single atomic operation (e.g. 512 bytes on most machines).
 *
 * Each DIRBLKSIZ byte block contains some number of directory entry
 * structures, which are of variable length.  Each directory entry has
 * a struct direct at the front of it, containing its inode number,
 * the length of the entry, and the length of the name contained in
 * the entry.  These are followed by the name padded to a 4 byte boundary
 * with null bytes.  All names are guaranteed null terminated.
 * The maximum length of a name in a directory is MAXNAMLEN.
 *
 * The macro DIRSIZ(dp) gives the amount of space required to represent
 * a directory entry.  Free space in a directory is represented by
 * entries which have dp->d_reclen >= DIRSIZ(dp).  All DIRBLKSIZ bytes
 * in a directory block are claimed by the directory entries.  This
 * usually results in the last entry in a directory having a large
 * dp->d_reclen.  When entries are deleted from a directory, the
 * space is returned to the previous entry in the same directory
 * block by increasing its dp->d_reclen.  If the first entry of
 * a directory block is free, then its dp->d_ino is set to 0.
 * Entries other than the first in a directory do not normally have
 * dp->d_ino set to 0.
 */
#define DIRBLKSIZ	512
#define	MAXNAMLEN	255

struct	direct {
	u_long	d_ino;			/* inode number of entry */
	u_short	d_reclen;		/* length of this record */
	u_short	d_namlen;		/* length of string in d_name */
	char	d_name[MAXNAMLEN + 1];	/* name must be no longer than this */
};

/*
 * The DIRSIZ macro gives the minimum record length which will hold
 * the directory entry.  This requires the amount of space in struct direct
 * without the d_name field, plus enough space for the name with a terminating
 * null byte (dp->d_namlen+1), rounded up to a 4 byte boundary.
 */
#undef DIRSIZ
#define DIRSIZ(dp) \
    ((sizeof (struct direct) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))

#ifndef KERNEL
/*
 * Definitions for library routines operating on directories.
 */
typedef struct _dirdesc {
	int	dd_fd;
	long	dd_loc;
	long	dd_size;
	char	dd_buf[DIRBLKSIZ];
} DIR;
#ifndef NULL
#define NULL 0
#endif
extern	DIR *opendir();
extern	struct direct *readdir();
extern	long telldir();
extern	void seekdir();
#define rewinddir(dirp)	seekdir((dirp), (long)0)
extern	void closedir();
#endif KERNEL
'End of ndir.h'
echo opendir.c
cat >opendir.c <<'End of opendir.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <ndir.h>

/*
 * open a directory.
 */
DIR *
opendir(name)
	char *name;
{
	register DIR *dirp;
	register int fd;
	struct stat sbuf;

	if ((fd = open(name, 0)) == -1)
		return NULL;
	fstat(fd, &sbuf);
	if (((sbuf.st_mode & S_IFDIR) == 0) ||
	    ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL)) {
		close (fd);
		return NULL;
	}
	dirp->dd_fd = fd;
	dirp->dd_loc = 0;
	return dirp;
}
'End of opendir.c'
echo quit.c
cat >quit.c <<'End of quit.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

int fail_status = 1;	/* Exit status on error */

#include <stdio.h>

extern char *program;

/* VARARGS1 */
quit(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
char *s;
{
	fflush(stdout);
	fprintf(stderr, "%s: fatal, ", program);
	fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
	fprintf(stderr, "\n");
	exit(fail_status);
}
'End of quit.c'
echo readdir.c
cat >readdir.c <<'End of readdir.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <sys/types.h>
#include <ndir.h>

/*
 * read an old stlye directory entry and present it as a new one
 */
#define	ODIRSIZ	14

struct	olddirect {
	ino_t	od_ino;
	char	od_name[ODIRSIZ];
};

/*
 * get next entry in a directory.
 */
struct direct *
readdir(dirp)
	register DIR *dirp;
{
	register struct olddirect *dp;
	static struct direct dir;

	for (;;) {
		if (dirp->dd_loc == 0) {
			dirp->dd_size = read(dirp->dd_fd, dirp->dd_buf,
			    DIRBLKSIZ);
			if (dirp->dd_size <= 0)
				return NULL;
		}
		if (dirp->dd_loc >= dirp->dd_size) {
			dirp->dd_loc = 0;
			continue;
		}
		dp = (struct olddirect *)(dirp->dd_buf + dirp->dd_loc);
		dirp->dd_loc += sizeof(struct olddirect);
		if (dp->od_ino == 0)
			continue;
		dir.d_ino = dp->od_ino;
		strncpy(dir.d_name, dp->od_name, ODIRSIZ);
		dir.d_name[ODIRSIZ] = '\0'; /* insure null termination */
		dir.d_namlen = strlen(dir.d_name);
		dir.d_reclen = DIRBLKSIZ;
		return (&dir);
	}
}
'End of readdir.c'
echo rename.c
cat >rename.c <<'End of rename.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

rename(from, to)
char *from;	/* Old name */
char *to;	/* New name */
{
	register status = -1;

	if (link(from, to) != -1 && unlink(from) != -1)
		status = 0;
	return(status);
}
'End of rename.c'
echo salloc.c
cat >salloc.c <<'End of salloc.c'
#include <stdio.h>

#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

extern char *malloc();
extern char *strcpy();

/*
 * Allocate and copy a string
 * into dynamic memory.
 */

char *
salloc(nam)
char *nam;
{
	register char *p;

	if ((p = malloc((unsigned)strlen(nam) + 1)) == NULL)
		quit("dynamic memory exhausted");
	return(strcpy(p, nam));
}
'End of salloc.c'
echo seekdir.c
cat >seekdir.c <<'End of seekdir.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <sys/param.h>
#include <ndir.h>

/*
 * seek to an entry in a directory.
 * Only values returned by "telldir" should be passed to seekdir.
 */
void
seekdir(dirp, loc)
	register DIR *dirp;
	long loc;
{
	long base, offset;
	struct direct *dp;

/* rti!trt: Always seek.  Slower, but safer. This may even fix a bug.
	if (loc == telldir(dirp))
		return;
 */
	base = loc & ~(DIRBLKSIZ - 1);
	offset = loc & (DIRBLKSIZ - 1);
	lseek(dirp->dd_fd, base, 0);
	dirp->dd_loc = 0;
	while (dirp->dd_loc < offset) {
		dp = readdir(dirp);
		if (dp == NULL)
			return;
	}
}
'End of seekdir.c'
echo sname.c
cat >sname.c <<'End of sname.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

char *
sname(nam)
register char *nam;
{
	register char *slash;

	slash = nam;
	while (*nam != '\0') {
		if (*nam++ == '/')
			slash = nam;
	}
	return(slash);
}
'End of sname.c'
echo telldir.c
cat >telldir.c <<'End of telldir.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <sys/types.h>
#include <ndir.h>

extern	long	lseek();	/* needed for pdp 11s -- ikonas!mcm */

/*
 * return a pointer into a directory
 */
long
telldir(dirp)
	DIR *dirp;
{
	return (lseek(dirp->dd_fd, 0L, 1) - dirp->dd_size + dirp->dd_loc);
}
'End of telldir.c'
echo umask.c
cat >umask.c <<'End of umask.c'
/*
 * Some systems do not have this
 * system call.
 *
 * DB Shimell	September 1984
 */

#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

umask(numask)
{
	return(0);
}
'End of umask.c'
echo warning.c
cat >warning.c <<'End of warning.c'
#ifndef lint
#ifndef NSCCS
static char sccsid[] = "%W%";
#endif
#endif

#include <stdio.h>

extern char *program;

unsigned wcount;

/* VARARGS1 */
warning(s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
char *s;
{
	wcount++;
	fflush(stdout);
	fprintf(stderr, "%s: warning, ", program);
	fprintf(stderr, s, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
	fprintf(stderr, "\n");
}
'End of warning.c'
exit 0
-- 

Regards,
Dave Shimell.  <shimell@stc.UUCP>
{root44, ukc, idec, stl, creed}!stc!shimell