/*
 *		Line printer daemon
 *
 * For version to use with Hydra dot-matrix printer, #define HYDRA
 */

/* File names */
char lpd[]	"/usr/lpd";
char lock[]	"/usr/lpd/lock";
#ifdef HYDRA
char printer[]	"/dev/pr";
#else
char printer[] "/dev/lp";
#endif
char afile[]	"/usr/adm/lpd";

#ifdef HYDRA
/*
 * control sequence to set unix default tabs on hydra
 */
char stabs[] {
	"\033G008016024032040048056064072080088096104112120128\n"
};

/* Tty modes for printer */
int ttymode[3] {
	(12<<8) | 12,		/* 4800 baud */
	0,
	020			/* CRMOD */
};
#endif

/* Buffers */
int cmdbuf[131];	/* for reading command file */
int prbuf[128];		/* for copying file to printer */
char lbuf[64];		/* for assembling lines from command file */
char dbuf[17];		/* for reading directory */
int tbuf[2];		/* for date & time */
int fout[131];

/* Accounting data */
struct {
	char	user[8];
	long	date;
	int	npages;
	int	nlines;
	int	nchars;
} ac;
int afd;

/* Signal routine to interrupt printer */

int	interrupted;

prstop()
{
	csignal(5, &prstop);
	interrupted = 1;
}

main()
{
	register flag;

	/* Ignore quit, interrupt, hangup */
	csignal(1, 1);
	csignal(2, 1);
	csignal(3, 1);

	/* Use lock to prevent multiple active daemons */
	if ((fout[0] = creat(lock, 0)) < 0)
		exit(0);
	/* Fork and exit, thus spawning the daemon */
	if (fork())
		exit(0);

	/* Write daemon's process-id to lock file for prstop command */
	printf("%o\n", getpid());
	flush();
	close(fout[0]);

	/* Use hangup signal to interrupt printout */
	csignal(5, &prstop);

	/* Before opening the printer, make sure there's something to print */
	close(0);
	close(1);
	if (open(lpd, 0) != 0)
		dexit();
	flag = 0;
	while (read(0, dbuf, 16) == 16)
		if ((dbuf[0] | dbuf[1]) != 0
		    && dbuf[2] == 'd' && dbuf[3] == 'f')
			flag++;
	if (!flag)
		dexit();

	/* Open the printer, and set proper tty modes */
	if (open(printer, 1) != 1)
		dexit();
	fout[0] = dup(1);
#ifdef HYDRA
	stty(1, ttymode);
	/* set hydra tabs */
	write(1, stabs, sizeof stabs);	/*** hydra ***/
#endif

	/* Open accounting file */
	if (afd = open(afile, 1))
		seek(afd, 0, 2);

	/* Search lpd directory for work to do */
	if (chdir(lpd) < 0)
		dexit();
	seek(0, 0, 0);
	while (read(0, dbuf, 16) == 16) {
		if ((dbuf[0] | dbuf[1]) == 0
		    || dbuf[2] != 'd' || dbuf[3] != 'f')
			continue;

		/* Process one command file */
		spool(&dbuf[2]);
		unlink(&dbuf[2]);

		/* Reread directory in case it changed while we were spooling */
		seek(0, 0, 0);
	}
	fflush(fout);

#ifdef HYDRA
	/* Feed paper partly out of printer */
	write(1, "\n\014", 2);
#endif

	dexit();
}

/*
 * Remove lock and terminate
 */
dexit()
{
	unlink(lock);
	exit(0);
}

/*
 * Read and obey a file of spooling commands
 */
spool(dfname)
char *dfname;
{
	register in, len, c;
	register char *p, *q;

	if (fopen(dfname, cmdbuf) < 0)
		return;
	for (p = &ac; p < (char *)&ac + sizeof ac; p++)
		*p = 0;
	time(tbuf);
	ac.date = tbuf[1];

	for(;;) {
		switch (c = getc(cmdbuf)) {
	
		/* EOF or unknown command */
		default:
			close(cmdbuf[0]);
			if (afd)
				write(afd, &ac, sizeof ac);
			return;
	
		/* Unsupported commands */
		case 'S':
		case 'M':
			getline(lbuf);
			continue;
	
		/* header page */
		case 'L':
			lbuf[0] = lbuf[1] = ' ';
			getline(&lbuf[2]);
			q = &lbuf[2];
			for (p = ac.user; p < &ac.user[8] && *q; p++)
				*p = *q++;
			header();
			continue;

		/* Copy file in binary ('F' means prepend a form feed */
		case 'F':
		case 'B':
			getline(lbuf);
			if ((in = open(lbuf, 0)) < 0)
				continue;
			prfile(in);
			if (c == 'F')
				putchar('\014');
			close(in);
			continue;
	
		/* Delete file after copying */
		case 'U':
			getline(lbuf);
			unlink(lbuf);
			continue;
		}
	}
}


/*
 * Read rest of line from command file
 */
getline(bufp)
char *bufp;
{
	register char *p;
	register c;

	for (p = bufp; (*p = c = getc(cmdbuf)) != '\n' && c > 0; p++)
		;
	*p = '\0';
}

/*
 * Print header page
 */

char hd0[] =
"========== University of Wollongong ========================\
==================== Computing Science Laboratory ==========\n\n\n";
char hd1[] =
"\n\n\n=============================================== ";
char hd2[] =
" ===============================================\n\014";

header()
{

	printf(hd0);
	bprint(lbuf);
	printf("%s%.24s%s", hd1, ctime(tbuf), hd2);
}

/*
 * Print file from <fd>, filtering out control chars and extra \f's
 */
char aout[] = "You can't print an _\ba._\bo_\bu_\bt file, stupid!\n";
prfile(fd)
register fd;
{
	register char *p;
	register len;

	/* Guard against idiots */
	if ((len = read(fd, prbuf, sizeof prbuf)) >= sizeof (int))
		switch (prbuf[0]) {
			case 0407:
			case 0410:
			case 0411:
				printf(aout);
				return;
		}
	while (len) {
		if (interrupted) {
			interrupted = 0;
			break;
		}
		for (p = prbuf; len; len--)
			putchar(*p++);
		len = read(fd, prbuf, sizeof prbuf);
	}
}

/*
 * Special version of putchar to filter out control chars and
 * keep track of page position
 */

int	line;		/* current line on page */
#define EJLINE	66	/* no. lines on page */

putchar(c)
register c;
{
	if (c < ' ') switch(c) {
		case '\n':
			ac.nlines++;
			if (++line >= EJLINE) {
				line = 0;
				ac.npages++;
			}
			break;

		case '\f':
			if (line == 0)
				return;
			line = 0;
			ac.npages++;
			break;

		case '\t':
		case '\b':
		case '\r':
		case '\016':
		case '\017':
			break;

		default:
			return;
	}
	ac.nchars++;
	putc(c, fout);
}
