#define CSPEED	(07<<8)|07
#define XTABS	02
#define LCASE	04
#define ECHO	010
#define CRMOD	020
#define ANYP	0300
#define CR1	010000
#define NPROC  50
#define NOFILE 15
#define SINGLE  7
#define REBOOT ~7
#define BUFSIZ  512
#define NTOKEN  5
#define NARGS   20
#define IPROCSZ 3
#define SIGHUP  1
#define LVL0  2
#define LVL1  3
#define LVL2  4
#define LVL3  5
#define LVL4  6
#define LVL5  7
#define LVL6  8
#define KILL  9
#define SINLVL 10
#define SIGSEG  11
#define SIGSYS  12
#define SIGPIPE 13
#define SIGCLK  14
#define SIGWRN  15
#define	ARESPAWN  21
#define AONCE     22
#define AWAIT	  23
#define	AOFF	  24
#define	ABOOT	  25
#define ABOOTWAIT 26
#define ADEMAND   27
struct	entryln {
	char	*lid;
	char	*rlevel;
	char	*action;
	char	*shellcm;
	char	**initcm;
}l;
struct iproc {
	int	id;
	char	flip;
	char	flop;
	int	ipid;
} iproc[NPROC];
struct stimulus {
	char	*keystr;
	int	numb;
};
struct utmp {
	char	name[8];
	int	line;
	int	time[2];
	int	upid;
}utmp;
int catchit,hboot,il,iq,rb,status;
int curlvl,lvltag,wrncnt;
char	lbuf[BUFSIZ+1];
char	*inarg[NARGS+1];
char	*CTTY	"/dev/ln00";
char	*SUSER	"/bin/sh";
char	*LINES	"/etc/lines";
char	*GETTY	"/etc/getty";
char	*SHELL	"/bin/sh";
char	*INIT	"/etc/init";
char	*UTMPF	"/etc/utmp";
char	*WTMPF	"/etc/wtmp";
char	*XINIT	"/etc/xinit";
char	set	1;
struct stimulus siggrp[] {
	"0",LVL0,
	"1",LVL1,
	"2",LVL2,
	"3",LVL3,
	"4",LVL4,
	"5",LVL5,
	"6",LVL6,
	"a",SIGSEG,
	"b",SIGSYS,
	"c",SIGPIPE,
	0
};
struct stimulus action[] {
	"respawn",ARESPAWN,
	"once",AONCE,
	"wait",AWAIT,
	"off",AOFF,
	"boot",ABOOT,
	"bootwait",ABOOTWAIT,
	"ondemand",ADEMAND,
	0
};
char *tabu[]{
	"xx",
	"RL",
	"!B",
	"OT",
	"NT",
	0
};
int cpackit[]{
	CSPEED,
	0,
	ANYP+XTABS+LCASE+CRMOD+ECHO+CR1
};
main()
{
	register i,runlvl,pid;
	setsig();
	lvltag = 0;
	i = creat(UTMPF,0644);
	copy("RL",&utmp.line);	/*initialize "RL" slot in UTMPF*/
	write(i,&utmp,16);
	close(i);
	acctg(-10,"**BOOT**");	/*record boot time*/
	if((i=getcsw()) == SINGLE) single();
	if(lvltag == 0){
		i = getcsw() + 2;  /* run level = signal-2 */
		if(i>=LVL0 && i<=LVL6) lvltag = i; 
			else lvltag = LVL0;
	}
/*
*	Main process path---steady state condition.
*/
	for(hboot=0;;) {
		setexit();
		sync();
		runlvl = 0;
		if(lvltag){
			runlvl = lvltag;
			lvltag = 0;
		}
		switch(runlvl){
		    case 0:  /*death of a child -rescan lines file*/
		    case SIGHUP: /*immediate scan of lines file*/
			runlvl = curlvl;
			break;
		    default:
			if(runlvl>=LVL0 && runlvl<=LVL6) 
			    if(runlvl != curlvl)
				if(cleanup(runlvl)) continue; /*err in cleanup*/
		}
		bringup(runlvl);
		sync();
		if(lvltag  && lvltag != SINLVL) continue; 
		for(;;){
			pid = wait(&status);
			if(pid == -1){  /*a signal was caught*/
				if(lvltag == SINLVL) {
					lvltag = 0;
					continue;
				}
				break;
			}
			if((i=find(pid,0))<0)continue; /*not in proc table*/
			rmproc(i);
			break;	/* iproc[i].flip == 0 ---rescan lines file and 
				   start up entry line process */
		}
	}
}
/*
*	If the run level is between LVL0 and LVL6,
*	cleanup will post a 20 second warning to all processes
*	running at a run level that is no longer current.  If
*	the warned process does not gracefully exit after 20 seconds 
*	have elapsed,the errant process is  forcibly terminated.
*	If runlvl is null then all processes in INIT's process 
*	table will be posted a 20 second warning and subsequently will
*	terminate or be terminated.
*	No other runlvls are possible.
*/
cleanup(runlvl)
{
	register int *wptr;
	register char *sptr;
	register pid;
	int i,j;
	int  warnid[NPROC];
	if(openlns()) {
		if(runlvl) single();
		return(1);	/*error return*/
	}
	wrncnt = 0;
	wptr = &warnid;
	zero(wptr,NPROC);
loop:
	while(j=getline()){
		if(j<0)continue;
		zmap();
		if((il=find(l.lid,1)) < 0) continue;
		if((l.rlevel == 0) && runlvl) continue; /*null field case*/
		if(runlvl){	/*check run level*/
			for(sptr=l.rlevel; *sptr; sptr++) {
				i = *sptr;
				i =& 0177;
				if((i=stoi(&i,&siggrp)) == 0) {
					console("Illegal rlevel at %s\n",l.lid);
					goto loop;
				}
				if(i == runlvl) goto loop;
			}
		}
		if(kill(iproc[il].ipid,SIGWRN) < 0) 
			zero(&iproc[il].id,IPROCSZ);
		else{
			wrncnt++;
			*wptr++ = iproc[il].ipid;
			iproc[il].flip = 20;
		}
	}
	alarm(20);
	while(wrncnt) {
		pid = wait(&status);
		if((pid != -1) && ((j=find(pid,0)) != -1)) {
			if(iproc[j].flip == 20 && wrncnt) --wrncnt;
			rmproc(j);
		}
	}
	alarm(0);
	for(wptr = &warnid; (*wptr && (wptr <= wptr[NPROC])); wptr++) {
		if((j=find(*wptr,0)) != -1) killproc(j);
	}
	close(0);
	return(0);	/*successful cleanup*/
}
/*
*	Legal runlvls honored by bringup are those falling
*	between LVL0 and LVL6 and between SIGSEG and SIGPIPE.
*	All others are invalid.
*/
bringup(runlvl)
{
	int i;
	register j;
	register char *sptr;
	if(openlns()) {
		single();
		return(1);
	}
	set =* -1;
	while(j=getline()){
		if(j<0)continue;	/* bad entry - ignore */
		if((iq = find(0,0)) < 0) {	/*index for free slot*/
			console("INIT process table filled(> %d)\n",NPROC);
			close(0);
			sleep(20);
			return(0);
		}
		zmap();	/* zero out pointers pointing to nulls */
		il = find(l.lid,1);	/* index for this process */
		if(il >= 0){	/*process exists*/
			iproc[il].flop = set;
			if(nullchk()) continue;
		}
		else{		/*process not in iproc table*/
			if(nullchk()){
				l.action = "respawn";
				copy(GETTY,&lbuf[4]); /*save l.lid ref*/
				for(j=4;lbuf[j];j++); /*find end of str*/
				lbuf[j++] = ' '; 	/*pad w/blank*/
				copy("ln",&lbuf[j]); j =+ 2;
				copy(l.lid,&lbuf[j]); /*append line id*/
				l.initcm = &lbuf[4];
				dfork(iq);
				continue;
			}
		}
		if(l.rlevel){ /*run level dependent entry*/
			for(sptr = l.rlevel; *sptr; sptr++) {
				i = *sptr;
				i =& 0177;
				if((i=stoi(&i,&siggrp)) == 0) {
				     console("Illegal rlevel at %s\n",l.lid);
				     break;
				}
				if(i == runlvl) goto ground;
			}
			if(il>=0 && runlvl>=LVL0 && runlvl<=LVL6) wrnkill(il);
			continue;
		}
	ground:
		if((i=stoi(l.action,&action)) == 0) {
			console("Illegal action at %s\n",l.lid);
			continue;
		}
		aloop(i,runlvl);
	}
	close(0);
	for(j=0;j<NPROC;j++){	/*remove processes deleted from lines file*/
		if(iproc[j].flop && (iproc[j].flop != set)) 
			wrnkill(j);
	}
	if(runlvl>=LVL0 && runlvl<=LVL6 && curlvl != runlvl){ 
		acctg(-runlvl,"RUNLEVEL");	/*record run level*/
		curlvl = runlvl;	/* reset the current level */
	}
	if(hboot == 0) hboot = 1;
	if(catchit) {/* catches secondary child deaths */
		catchit = 0;
		reset();  
	}
	return(0);
}
/*	
*	Interpret action field and perform  the predefined
*	actions.
*/
aloop(actnum,runlvl)
{
	register i;
	switch(actnum) {
		case ADEMAND:
		case ARESPAWN:	/* start the process and do not wait for
				   its completion */
			if(il<0) dfork(iq);   /* create a process */
			return;		/* ignore--process is alive */
		case AONCE:	/* start the process once and clear it from  
				   init's process table when it dies */
			if(curlvl == runlvl) return;
			dfork(iq);
			return;
		case AWAIT:	/* start the process and wait for it! */
			if(curlvl == runlvl) return;
			waitfor(iq);
			return;
		case AOFF:	/* Ignore this process and continue on.
				   if process is active then terminate it
				   and clear the entry from the proc table. 
				   A 20 second warning is posted before it
				   is forcibly terminated. */
			if(il < 0) return;
			wrnkill(il);
			return;
		case ABOOT:	/* start this process only at boot time
				   ignore on any subsequent scan of this */
			if(hboot == 0) dfork(iq);
			return;
		case ABOOTWAIT:	/*same as ABOOT but wait for it*/
			if(hboot == 0) waitfor(iq);
			return;
	}
}
/*
*	Wait for the process to complete
*/
waitfor(enum)
{
	dfork(enum);
	if(waitproc(iproc[enum].ipid)) rmproc(enum);
}
/*
*	Create/re-create processes via a fork.
*	Parent process stores in the proc table the process id of the 
*	child and the entry tag(lid). 
*	The parent then returns without waiting for the child to complete.
*/
dfork(enum)
{
	register char **reserve;
	register char  *atty;
	register type;
	type = -1;
	if(enum < 0) {
		console("%d is an invalid iproc table entry\n",enum);
		return;
	}
	if(l.shellcm) type = 0;
	if(l.initcm){
		type = 2;
		getarg();
		if(cmp(*l.initcm,GETTY)) {
			for(reserve=tabu;*reserve;reserve++){
				if(cmp(l.lid,*reserve) == 0) continue;
				console("line id %s reserved\n",l.lid);
				return;
			}
			type = 1;
		}
	}
	copy(l.lid,&iproc[enum].id);
	iproc[enum].flip = 0;
	iproc[enum].flop = set;
	if((iproc[enum].ipid = fork()) == 0) {
		close(0);close(1);close(2);
		if(l.shellcm) shcmnd();
		if(l.initcm) incmnd();
		exit(1);
	}
	switch(type){
		case 0:
			acctg(enum,l.shellcm);
			break;
		case 1:
			acctg(enum,1);
			break;
		case 2:
			acctg(enum,*l.initcm);
	}
}
/*
*	The shell command interpreter is invoked with the
*	'-c' option.
*/
shcmnd()
{
	register i;
	if((i=fork()) == 0) {
		open("/dev/null",0);
		open(CTTY,1);
		stty(1,cpackit);
		execl("/bin/sh","-","-c",l.shellcm,0);
		console("%s  not executed\n",l.shellcm);
		sleep(20);
		exit(1);
	}
	waitproc(i);
}
/*
*	A check is performed on the first character of an argument field
*	to see if the standard input and output is to be changed.
*	If it is then a change is initiated and the process is
*	executed.  Incmnd assumes that the process and its arguments
*	are in a vector beginning at inarg.
*/
incmnd()
{
	register char **k,*infile,*outfile;
	k = l.initcm;
	while(*k) {
		switch(**k) {
			case '<' :   /* change standard input */
				if(k[0][1] == 0){
					*k++ = 0;
					infile = *k;
				}
				else  infile = ++(*k);
				*k++ = 0;
				close(0);
				if((open(infile,2)) < 0){
					console("bad input file %s:%s\n",
						l.lid,infile);
					sleep(20);
					exit(1);
				}
				break;
			case '>' :   /* change standard output */
				if(k[0][1] == 0){
					*k++ = 0;
					outfile = *k;
				}
				else  outfile = ++(*k);
				*k++ = 0;
				close(1);
				switch(open(outfile,2)) {
					case 0:
						dup(0);
						close(0);
						break;
					case -1:
						console("bad out file %s:%s\n",
							l.lid,outfile);
						sleep(20);
						exit(1);
				}
				break;
			default :
				k++;
		}
	}
	execv(*l.initcm,l.initcm);
	console("%s  not executed\n",*l.initcm);
	sleep(20);
}
/*
*	Invoke single user mode on the console tty.
*/
single()
{
	register i,j;
	curlvl = rb = 0;
	if(lvltag) clrsys();
	acctg(-1,"RUNLEVEL");	/*single user recording*/
	for(;;) {
		console("SINGLE USER MODE\n");
		if((rb=fork()) == 0) {
			close(0);close(1);close(2);
			open(CTTY,2);
			dup(0);
			stty(1,cpackit);
			execl(SUSER,"-",0);
			console("Exec of %s failed\n",SUSER);
			exit(1);
		}
		while(rb != (j=wait(&status))) {
			if(j == -1){
				if(lvltag>=LVL0 && lvltag <= LVL6){
					kill(rb,KILL);
					if(hboot) reset();
					return;
				}
				lvltag = 0;
			}
		}
		if((i=bus()) != SINGLE && i != REBOOT){
			i =+ 2;
			if(i>=LVL0 && i<=LVL6) lvltag = i;
				else lvltag = LVL0;
			if(hboot) reset();
			return; /*valid only at boot time*/
		}
	}
}
/*
*	The bus signal (10) is trapped to this function.
*	The console switches are checked and either the
*	single user or reboot sequence is started up.
*/
bus()
{
	register j;
	register struct iproc *p;
	signal(lvltag=SINLVL,bus);
	switch(j=getcsw()){
		case SINGLE:
			if(curlvl) single();
			break;  /*if already in single user*/
		case REBOOT:
			if(curlvl){
				console("Not single user-reboot disallowed\n");
				break;
			}
			clrsys();
			if(rb)kill(rb,KILL);	/*kill SU console tty*/
			console("Overlay with %s\n",XINIT);
			sleep(5); /*delay for switch setting chng*/
			execl(XINIT,XINIT,0);
			console("Overlay with %s failed\n",XINIT);
			if(rb == 0) single();
			break;
	}
	return(j);
}
/*
*	Record the accounting information on all processes
*	when they spawn and die.  the utmpf file will always have
*	an instantaneous record of process activities; while the wtmpf
*	file(if it is turned on) will contain the running history of 
*	the process activities from the time WTMPF file was turned on.
*		ENUM	MARKER		REASON
*		>=0	0		dead process
*		>=0	1		line process
*		>=0	string		non-line process
*		-1	RUNLEVEL	single user
*		-(2->8)	RUNLEVEL	run levels
*		-10	**BOOT**	hardware boot
*/
acctg(enum,marker)
char *marker;
{
	register rw,k;
	register struct utmp *p;
	int cbuf[256];
	char *cp;
	int key;
	zero(&utmp,(sizeof utmp)/2);
	switch(marker){
		default:	/*non-line process accounting*/
			cp = marker;
			while(*cp) /*strip off leading pathnames*/
				if(*cp++ == '/') marker = cp;
			copy(marker,utmp.name);
			if(enum < 0){
				if(enum == -10){
					copy("!B",&key);
					utmp.upid = 0;
				}
				else{ /*(-1,-8)*/
					copy("RL",&key);
					if(enum != -1)
						enum = -(enum) - 2;
					utmp.upid = enum;
				}
				break;
			}
		case 1:		/*line process accounting*/
			utmp.upid = iproc[enum].ipid;
		case 0:		/*dead process accounting*/
			key = iproc[enum].id;
	}
	utmp.line = key;
	time(utmp.time);
	if((rw=open(UTMPF,2)) < 0) goto out;
	while((k=read(rw,&cbuf,512)) > 0){
		cp = k; /*save original count*/
		for(p = &cbuf;(k =- 16) >= 0; p++){ /*does it exist?*/
			if(p->line != key) continue;
			seek(rw,-(k+16),1);
			write(rw,&utmp,16);
			goto out;
		}
		if(cp < 512) break; /*entire utmpf file examined*/
	}
	seek(rw,0,0);
	while((k=read(rw,&cbuf,512)) > 0){
		cp = k; /*save original count*/
		for(p = &cbuf;(k =- 16)>=0; p++){ /*look for empty slot*/
			if(p->line != 0) continue;
			seek(rw,-(k+16),1);
			write(rw,&utmp,16);
			goto out;
		}
		if(cp < 512) break;
	}
	if(k <= 0) write(rw,&utmp,16); /* boundary case conditions
					* where '<' implies the end
					* of the file but < 512
					*/
out:
	close(rw);
	if((rw=open(WTMPF,1)) >= 0) {
		seek(rw,0,2);
		write(rw,&utmp,16);
		close(rw);
	}
	sync();
}
/*
*	Lines file opening strategy
*/
openlns()
{
	register i;
	switch(i=open(LINES,0)){
	    case 0:
		break;
	    default:
		close(i);
		sleep(1);
		switch(i=open(LINES,0)){
		    case 0:
			break;
		    default:
			close(i);
			sleep(3);
			switch(i=open(LINES,0)){
			    case 0:
				break;
			    default:
				close(i);
				console("open error on %s\n",LINES);
				return(1); 
			}
			break;
		}
		break;
	}
	return(0);  /*successful open*/
}
/*
*	Getline filters out comments,maps multiple blanks to one
*	blank and stores into a previously nulled-out 5 element array
*	pointers to non-null fields(defined as tokens between colons on 
*	the entry line).  Getline parses an entry until a line feed is
*	encountered and subsequently returns a 1.  A -1 is returned on
*	a syntax infraction and a 0 on the end of file for /etc/lines.
*/
getline()
{
	register char c,*q;
	register int *p;
	char  c0;
	int fl,keepc;
	fl = 1;
	zero(&lbuf,BUFSIZ/2);  /* zero out lbuf char array(512) */
	zero(&l,NTOKEN);  /* zero out array of pointers -struct "l" */
	p = &l;
	q = &lbuf;
	keepc = ' ';
	*p = q;
	while((c = getchar()) > 0) {
		if(q > &lbuf[BUFSIZ]) {
			console("%d buffer size exceeded\n",BUFSIZ);
			while((c=getchar()) != '\n'); /*goto end of line*/
			return(-1);
		}
		switch(c) {
		    case'/':	/* comments */
			c0 = comments();
			if(c0 == 0)return(-1);
			if(c0 == '\n'){
				*q++ = c;
				goto err;
			}
			if(c0 != '*'){
				*q++ = c;
				*q++ = c0;
			}
			continue;
		    case ':':
			if(fl == 1 && q>(&lbuf[2])) goto err;
			*q = '\0';
			*(++p) = ++q;
			keepc = ' ';
			if(fl++ > NTOKEN) {
				console("over %d token limit\n%s\n",
					NTOKEN,&lbuf);
				goto err;
			}
			continue;
		    case '\n':	/* end of line or continuation */
			if(keepc == '\\') {
				q--;
				continue;
			}
			*q = '\0';
			if(fl == 1) goto err;
			return(1);
		    case '\t':	/* suppress horiz tabs */
			continue;
		    case ' ':	/* all excess blanks reduced to one*/
			if(keepc == ' ') continue;
		    default:
			*q++=c;
			keepc = c;
		}
	}
	return(0);
err:
	console("syntax violation in LINES file entry at %s\n",l.lid);
	return(-1);
}
/*
*	supress comments in lines files
*/
comments()
{
	register char c;
	if((c=getchar())!='*')return(c);
	while((c=getchar())>0)
		if(c=='*' && (c=getchar())=='/')return('*');
	console("syntax violation  at %s\n",l.lid);
	return(0);
}
/*
*	Parsing a given token with delimiters '\0',' '.
*	Pointers to the various arguments in the initcm token
*	fields are stored in an array of pointers(inarg).
*	Because of getline's parsing no more than one
*	blank between characters exist.
*/
getarg()
{
	register char *s;
	register int  *d,ctr;
	ctr = 1;
	d = &inarg;	/*ptr to array of argument ptrs*/
	s = l.initcm;	/* ptr to lbuf[] element */
	l.initcm = d;
	zero(inarg,NARGS);	/* zero out array of pointers */
	*d++ = s;
	while(*s) {
		switch(*s) {
			case ' ':
				*s++ = '\0';
				*d++ = s++;
				if(ctr++ > NARGS) {
					console("%d args exceeded\n",NARGS);
					console("%s not executed\n",*l.initcm);
					return(-1);
				}
				break;
			default:
				s++;
		}
	}
}
/*
*	Close all files and clear the system with due warning
*/
clrsys()
{
	register i;
	for(i=0;i<NOFILE;i++) close(i);
	if(cleanup(0)) for(i=0;i<NPROC;i++) killproc(i);
}
/*
*	warn the process of impending doom then terminate the
*	process if it is still alive after 20 seconds.
*/
wrnkill(enum)
{
	register i;
	if(fork() == 0){ /*shadow process*/
		kill(iproc[enum].ipid,SIGWRN); /*object process*/
		sleep(20);
		kill(iproc[enum].ipid,KILL);
		exit(1);
	}
	alarm(2); /* 2 second timer */
	if(waitproc(iproc[enum].ipid)){
		alarm(0);
		rmproc(enum);
	}
}
/*
*	clear a process from the init proc table
*/
killproc(enum)
{
	if(iproc[enum].ipid)
		if(kill(iproc[enum].ipid,KILL)>=0) rmproc(enum);
}
/*
*	The process id is passed to waitproc and a wait is effected
*	until a process id or a signal is caught.  If the object
*	process dies a 1 is returned, if a signal is caught a 0 is returned.
*	if a secondary process(other than the desired process in question) 
*	dies waitproc checks if it is one of INIT's child and if it is
*	removes it, sets the catchit flag and continues to wait.
*	If the process is not a child of INIT then it just continues
*	to wait.
*/
waitproc(pnum)
{
	register i;
	while((i=wait(&status)) != pnum){
		if(i<0) return(0); /*a signal was caught*/
		if((i=find(i,0)) < 0) continue;
		++catchit;	/*secondary process died*/
		rmproc(i);
	}
	return(1);	/*successful wait on primary process*/
}
/*
*	process is removed from init's internal process table
*/
rmproc(enum)
{
	if(iproc[enum].ipid){
		acctg(enum,0);
		zero(&iproc[enum].id,IPROCSZ);
	}
}
/*	If a pointer addresses a value of zero(\0) then that
*	pointer is zeroed out.
*/
zmap()
{
	register i;
	register char **k;
	k = &l;
	k++;	/* begin at l.rlevel (offset 1) */
	for(i=0; i<NTOKEN-1; i++) {
		if(k[0][0] == '\0') *k = 0;
		k++;
	}
}
/*
*	Searches proc table(element id or ipid) for  value  and 
*	returns the index of entry on a successful match.  Otherwise
*	a -1 is returned.
*/
find(value,flag)
{
	register i,*iptr;
	register struct iproc *p;
	if(flag){	/*search for id*/
		p = &iproc[0].id;
		iptr = value;
		value = *iptr;
	}
	else	/*search for ipid*/
		p = &iproc[0].ipid;
	for(i=0;i<NPROC;i++)if((p++)->id == value)return(i);
	return(-1);
}
/*
*	Check the l.rlevel,l.action,l.shellcm and l.initcm fields
*	for null values.  If they are all null then return 1.
*/
nullchk()
{
	register i,j,*k;
	k = &l;
	j = 0;
	k++;	/* begin at offset 1 */
	for(i=0;i<NTOKEN-1; i++) j =+ *k++;
	if(j == 0) return(1);
	return(0);
}
/*	
*	a = object string
*	b = address of structure "a" is to be searched against
*
*	Stoi will return a number associated with a predefined string 
*	upon a successful scan; otherwise a -1 is returned,
*	the entry ignored and INIT continues scanning.
*/
stoi(a,b)
char *a;
struct	stimulus *b;
{
	for(b;b->numb;b++)
		if(cmp(a,b->keystr)) return(b->numb);
	return(0);
}
/*
*	1 = successful string compare
*	0 = failure on string compare
*/
cmp(a,b)
char *a,*b;
{
	while(*a++ == *b) {
		if (*b++ == '\0') return(1);
	}
	return(0);
}
/*
*	copy string s to string t
*/
copy(s,t)
char *s,*t;
{
	while(*t++ = *s++);
}
/*
*	Zeroes out the given array.  Note where character arrays
*	are involved the limit passed must be one-half the size
*	of the target array.
*/
zero(array,limit)
int  *array;
{
	register i;
	for(i=0; i<limit; i++) *array++ = 0;
}
/*
*	print message to console tty
*/
console(p,arg1,arg2,arg3,arg4,arg5)
char *p;
{
extern	fout;
	register i;
	if((i=fork()) == 0) {
		for(i=0;i<3;i++) close(i);
		open(CTTY,2);
		dup(0);
		stty(1,cpackit);
		write(1,"INIT: ",6);
		printf(p,arg1,arg2,arg3,arg4,arg5);
		flush();	/* purge the print buffer */
		exit(1);
	}
	waitproc(i);
}
setsig()
{
	extern bus();
	hangup();
	intr();
	quit();
	ilgins();
	trace();
	iot();
	emt();
	fpt();
	signal(SINLVL,bus);
	seg();
	sys();
	pipe();
	clk();
}
/*
*	Signal catching and flagging.
*/
hangup()
{	signal(lvltag = SIGHUP,hangup);}
intr()
{	signal(lvltag = LVL0,intr);}
quit()
{	signal(lvltag = LVL1,quit);}
ilgins()
{	signal(lvltag = LVL2,ilgins);}
trace()
{	signal(lvltag = LVL3,trace);}
iot()
{	signal(lvltag = LVL4,iot);}
emt()
{	signal(lvltag = LVL5,emt);}
fpt()
{	signal(lvltag = LVL6,fpt);}
seg()
{	signal(lvltag = SIGSEG,seg);}
sys()
{	signal(lvltag = SIGSYS,sys);}
pipe()
{	signal(lvltag = SIGPIPE,pipe);}
clk()
{	signal(SIGCLK,clk);wrncnt=0;}
