printjob.c revision 5445
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1983, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes *
61553Srgrimes * Redistribution and use in source and binary forms, with or without
71553Srgrimes * modification, are permitted provided that the following conditions
81553Srgrimes * are met:
91553Srgrimes * 1. Redistributions of source code must retain the above copyright
101553Srgrimes *    notice, this list of conditions and the following disclaimer.
111553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121553Srgrimes *    notice, this list of conditions and the following disclaimer in the
131553Srgrimes *    documentation and/or other materials provided with the distribution.
141553Srgrimes * 3. All advertising materials mentioning features or use of this software
151553Srgrimes *    must display the following acknowledgement:
161553Srgrimes *	This product includes software developed by the University of
171553Srgrimes *	California, Berkeley and its contributors.
181553Srgrimes * 4. Neither the name of the University nor the names of its contributors
191553Srgrimes *    may be used to endorse or promote products derived from this software
201553Srgrimes *    without specific prior written permission.
211553Srgrimes *
221553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
231553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
241553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
251553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
261553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
271553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
281553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
291553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
301553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
311553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
321553Srgrimes * SUCH DAMAGE.
331553Srgrimes */
341553Srgrimes
351553Srgrimes#ifndef lint
361553Srgrimesstatic char copyright[] =
371553Srgrimes"@(#) Copyright (c) 1983, 1993\n\
381553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
391553Srgrimes#endif /* not lint */
401553Srgrimes
411553Srgrimes#ifndef lint
421553Srgrimesstatic char sccsid[] = "@(#)printjob.c	8.2 (Berkeley) 4/16/94";
431553Srgrimes#endif /* not lint */
441553Srgrimes
451553Srgrimes
461553Srgrimes/*
471553Srgrimes * printjob -- print jobs in the queue.
481553Srgrimes *
491553Srgrimes *	NOTE: the lock file is used to pass information to lpq and lprm.
501553Srgrimes *	it does not need to be removed because file locks are dynamic.
511553Srgrimes */
521553Srgrimes
531553Srgrimes#include <sys/param.h>
541553Srgrimes#include <sys/wait.h>
551553Srgrimes#include <sys/stat.h>
561553Srgrimes#include <sys/types.h>
571553Srgrimes
581553Srgrimes#include <pwd.h>
591553Srgrimes#include <unistd.h>
601553Srgrimes#include <signal.h>
611553Srgrimes#include <sgtty.h>
621553Srgrimes#include <syslog.h>
631553Srgrimes#include <fcntl.h>
641553Srgrimes#include <dirent.h>
651553Srgrimes#include <errno.h>
661553Srgrimes#include <stdio.h>
671553Srgrimes#include <string.h>
681553Srgrimes#include <stdlib.h>
691553Srgrimes#include "lp.h"
701553Srgrimes#include "lp.local.h"
711553Srgrimes#include "pathnames.h"
721553Srgrimes#include "extern.h"
731553Srgrimes
741553Srgrimes#define DORETURN	0	/* absorb fork error */
751553Srgrimes#define DOABORT		1	/* abort if dofork fails */
761553Srgrimes
771553Srgrimes/*
781553Srgrimes * Error tokens
791553Srgrimes */
801553Srgrimes#define REPRINT		-2
811553Srgrimes#define ERROR		-1
821553Srgrimes#define	OK		0
831553Srgrimes#define	FATALERR	1
841553Srgrimes#define	NOACCT		2
851553Srgrimes#define	FILTERERR	3
861553Srgrimes#define	ACCESS		4
871553Srgrimes
881553Srgrimesstatic dev_t	 fdev;		/* device of file pointed to by symlink */
891553Srgrimesstatic ino_t	 fino;		/* inode of file pointed to by symlink */
901553Srgrimesstatic FILE	*cfp;		/* control file */
911553Srgrimesstatic int	 child;		/* id of any filters */
921553Srgrimesstatic int	 lfd;		/* lock file descriptor */
931553Srgrimesstatic int	 ofd;		/* output filter file descriptor */
941553Srgrimesstatic int	 ofilter;	/* id of output filter, if any */
951553Srgrimesstatic int	 pfd;		/* prstatic inter file descriptor */
961553Srgrimesstatic int	 pid;		/* pid of lpd process */
971553Srgrimesstatic int	 prchild;	/* id of pr process */
981553Srgrimesstatic int	 remote;	/* true if sending files to remote */
991553Srgrimesstatic char	 title[80];	/* ``pr'' title */
1001553Srgrimesstatic int	 tof;		/* true if at top of form */
1011553Srgrimes
1021553Srgrimesstatic char	class[32];		/* classification field */
1031553Srgrimesstatic char	fromhost[32];		/* user's host machine */
1041553Srgrimes				/* indentation size in static characters */
1051553Srgrimesstatic char	indent[10] = "-i0";
1061553Srgrimesstatic char	jobname[100];		/* job or file name */
1071553Srgrimesstatic char	length[10] = "-l";	/* page length in lines */
1081553Srgrimesstatic char	logname[32];		/* user's login name */
1091553Srgrimesstatic char	pxlength[10] = "-y";	/* page length in pixels */
1101553Srgrimesstatic char	pxwidth[10] = "-x";	/* page width in pixels */
1111553Srgrimesstatic char	tempfile[] = "errsXXXXXX"; /* file name for filter output */
1121553Srgrimesstatic char	width[10] = "-w";	/* page width in static characters */
1131553Srgrimes
1141553Srgrimesstatic void       abortpr __P((int));
1151553Srgrimesstatic void       banner __P((char *, char *));
1161553Srgrimesstatic int        dofork __P((int));
1171553Srgrimesstatic int        dropit __P((int));
1181553Srgrimesstatic void       init __P((void));
1191553Srgrimesstatic void       openpr __P((void));
1201553Srgrimesstatic int        print __P((int, char *));
1211553Srgrimesstatic int        printit __P((char *));
1221553Srgrimesstatic void       pstatus __P((const char *, ...));
1231553Srgrimesstatic char       response __P((void));
1241553Srgrimesstatic void       scan_out __P((int, char *, int));
1251553Srgrimesstatic char      *scnline __P((int, char *, int));
1261553Srgrimesstatic int        sendfile __P((int, char *));
1271553Srgrimesstatic int        sendit __P((char *));
1281553Srgrimesstatic void       sendmail __P((char *, int));
1291553Srgrimesstatic void       setty __P((void));
1301553Srgrimes
1311553Srgrimesvoid
1321553Srgrimesprintjob()
1331553Srgrimes{
1341553Srgrimes	struct stat stb;
1351553Srgrimes	register struct queue *q, **qp;
1361553Srgrimes	struct queue **queue;
1371553Srgrimes	register int i, nitems;
1381553Srgrimes	long pidoff;
1391553Srgrimes	int count = 0;
1401553Srgrimes
1411553Srgrimes	init();					/* set up capabilities */
1421553Srgrimes	(void) write(1, "", 1);			/* ack that daemon is started */
1431553Srgrimes	(void) close(2);			/* set up log file */
1441553Srgrimes	if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
1451553Srgrimes		syslog(LOG_ERR, "%s: %m", LF);
1461553Srgrimes		(void) open(_PATH_DEVNULL, O_WRONLY);
1471553Srgrimes	}
1481553Srgrimes	setgid(getegid());
1491553Srgrimes	pid = getpid();				/* for use with lprm */
1501553Srgrimes	setpgrp(0, pid);
1511553Srgrimes	signal(SIGHUP, abortpr);
1521553Srgrimes	signal(SIGINT, abortpr);
1531553Srgrimes	signal(SIGQUIT, abortpr);
1541553Srgrimes	signal(SIGTERM, abortpr);
1551553Srgrimes
1561553Srgrimes	(void) mktemp(tempfile);
1571553Srgrimes
1581553Srgrimes	/*
1591553Srgrimes	 * uses short form file names
1601553Srgrimes	 */
1611553Srgrimes	if (chdir(SD) < 0) {
1621553Srgrimes		syslog(LOG_ERR, "%s: %m", SD);
1631553Srgrimes		exit(1);
1641553Srgrimes	}
1651553Srgrimes	if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
1661553Srgrimes		exit(0);		/* printing disabled */
1671553Srgrimes	lfd = open(LO, O_WRONLY|O_CREAT, 0644);
1681553Srgrimes	if (lfd < 0) {
1691553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1701553Srgrimes		exit(1);
1711553Srgrimes	}
1721553Srgrimes	if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
1731553Srgrimes		if (errno == EWOULDBLOCK)	/* active deamon present */
1741553Srgrimes			exit(0);
1751553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1761553Srgrimes		exit(1);
1771553Srgrimes	}
1781553Srgrimes	ftruncate(lfd, 0);
1791553Srgrimes	/*
1801553Srgrimes	 * write process id for others to know
1811553Srgrimes	 */
1821553Srgrimes	sprintf(line, "%u\n", pid);
1831553Srgrimes	pidoff = i = strlen(line);
1841553Srgrimes	if (write(lfd, line, i) != i) {
1851553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, LO);
1861553Srgrimes		exit(1);
1871553Srgrimes	}
1881553Srgrimes	/*
1891553Srgrimes	 * search the spool directory for work and sort by queue order.
1901553Srgrimes	 */
1911553Srgrimes	if ((nitems = getq(&queue)) < 0) {
1921553Srgrimes		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
1931553Srgrimes		exit(1);
1941553Srgrimes	}
1951553Srgrimes	if (nitems == 0)		/* no work to do */
1961553Srgrimes		exit(0);
1971553Srgrimes	if (stb.st_mode & 01) {		/* reset queue flag */
1981553Srgrimes		if (fchmod(lfd, stb.st_mode & 0776) < 0)
1991553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
2001553Srgrimes	}
2011553Srgrimes	openpr();			/* open printer or remote */
2021553Srgrimesagain:
2031553Srgrimes	/*
2041553Srgrimes	 * we found something to do now do it --
2051553Srgrimes	 *    write the name of the current control file into the lock file
2061553Srgrimes	 *    so the spool queue program can tell what we're working on
2071553Srgrimes	 */
2081553Srgrimes	for (qp = queue; nitems--; free((char *) q)) {
2091553Srgrimes		q = *qp++;
2101553Srgrimes		if (stat(q->q_name, &stb) < 0)
2111553Srgrimes			continue;
2121553Srgrimes	restart:
2131553Srgrimes		(void) lseek(lfd, (off_t)pidoff, 0);
2141553Srgrimes		(void) sprintf(line, "%s\n", q->q_name);
2151553Srgrimes		i = strlen(line);
2161553Srgrimes		if (write(lfd, line, i) != i)
2171553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, LO);
2181553Srgrimes		if (!remote)
2191553Srgrimes			i = printit(q->q_name);
2201553Srgrimes		else
2211553Srgrimes			i = sendit(q->q_name);
2221553Srgrimes		/*
2231553Srgrimes		 * Check to see if we are supposed to stop printing or
2241553Srgrimes		 * if we are to rebuild the queue.
2251553Srgrimes		 */
2261553Srgrimes		if (fstat(lfd, &stb) == 0) {
2271553Srgrimes			/* stop printing before starting next job? */
2281553Srgrimes			if (stb.st_mode & 0100)
2291553Srgrimes				goto done;
2301553Srgrimes			/* rebuild queue (after lpc topq) */
2311553Srgrimes			if (stb.st_mode & 01) {
2321553Srgrimes				for (free((char *) q); nitems--; free((char *) q))
2331553Srgrimes					q = *qp++;
2341553Srgrimes				if (fchmod(lfd, stb.st_mode & 0776) < 0)
2351553Srgrimes					syslog(LOG_WARNING, "%s: %s: %m",
2361553Srgrimes						printer, LO);
2371553Srgrimes				break;
2381553Srgrimes			}
2391553Srgrimes		}
2401553Srgrimes		if (i == OK)		/* file ok and printed */
2411553Srgrimes			count++;
2421553Srgrimes		else if (i == REPRINT) { /* try reprinting the job */
2431553Srgrimes			syslog(LOG_INFO, "restarting %s", printer);
2441553Srgrimes			if (ofilter > 0) {
2451553Srgrimes				kill(ofilter, SIGCONT);	/* to be sure */
2461553Srgrimes				(void) close(ofd);
2471553Srgrimes				while ((i = wait(0)) > 0 && i != ofilter)
2481553Srgrimes					;
2491553Srgrimes				ofilter = 0;
2501553Srgrimes			}
2511553Srgrimes			(void) close(pfd);	/* close printer */
2521553Srgrimes			if (ftruncate(lfd, pidoff) < 0)
2531553Srgrimes				syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
2541553Srgrimes			openpr();		/* try to reopen printer */
2551553Srgrimes			goto restart;
2561553Srgrimes		}
2571553Srgrimes	}
2581553Srgrimes	free((char *) queue);
2591553Srgrimes	/*
2601553Srgrimes	 * search the spool directory for more work.
2611553Srgrimes	 */
2621553Srgrimes	if ((nitems = getq(&queue)) < 0) {
2631553Srgrimes		syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
2641553Srgrimes		exit(1);
2651553Srgrimes	}
2661553Srgrimes	if (nitems == 0) {		/* no more work to do */
2671553Srgrimes	done:
2681553Srgrimes		if (count > 0) {	/* Files actually printed */
2691553Srgrimes			if (!SF && !tof)
2701553Srgrimes				(void) write(ofd, FF, strlen(FF));
2711553Srgrimes			if (TR != NULL)		/* output trailer */
2721553Srgrimes				(void) write(ofd, TR, strlen(TR));
2731553Srgrimes		}
2741553Srgrimes		(void) unlink(tempfile);
2751553Srgrimes		exit(0);
2761553Srgrimes	}
2771553Srgrimes	goto again;
2781553Srgrimes}
2791553Srgrimes
2801553Srgrimeschar	fonts[4][50];	/* fonts for troff */
2811553Srgrimes
2821553Srgrimeschar ifonts[4][40] = {
2831553Srgrimes	_PATH_VFONTR,
2841553Srgrimes	_PATH_VFONTI,
2851553Srgrimes	_PATH_VFONTB,
2861553Srgrimes	_PATH_VFONTS,
2871553Srgrimes};
2881553Srgrimes
2891553Srgrimes/*
2901553Srgrimes * The remaining part is the reading of the control file (cf)
2911553Srgrimes * and performing the various actions.
2921553Srgrimes */
2931553Srgrimesstatic int
2941553Srgrimesprintit(file)
2951553Srgrimes	char *file;
2961553Srgrimes{
2971553Srgrimes	register int i;
2981553Srgrimes	char *cp;
2991553Srgrimes	int bombed = OK;
3001553Srgrimes
3011553Srgrimes	/*
3021553Srgrimes	 * open control file; ignore if no longer there.
3031553Srgrimes	 */
3041553Srgrimes	if ((cfp = fopen(file, "r")) == NULL) {
3051553Srgrimes		syslog(LOG_INFO, "%s: %s: %m", printer, file);
3061553Srgrimes		return(OK);
3071553Srgrimes	}
3081553Srgrimes	/*
3091553Srgrimes	 * Reset troff fonts.
3101553Srgrimes	 */
3111553Srgrimes	for (i = 0; i < 4; i++)
3121553Srgrimes		strcpy(fonts[i], ifonts[i]);
3131553Srgrimes	sprintf(&width[2], "%d", PW);
3141553Srgrimes	strcpy(indent+2, "0");
3151553Srgrimes
3161553Srgrimes	/*
3171553Srgrimes	 *      read the control file for work to do
3181553Srgrimes	 *
3191553Srgrimes	 *      file format -- first character in the line is a command
3201553Srgrimes	 *      rest of the line is the argument.
3211553Srgrimes	 *      valid commands are:
3221553Srgrimes	 *
3231553Srgrimes	 *		S -- "stat info" for symbolic link protection
3241553Srgrimes	 *		J -- "job name" on banner page
3251553Srgrimes	 *		C -- "class name" on banner page
3261553Srgrimes	 *              L -- "literal" user's name to print on banner
3271553Srgrimes	 *		T -- "title" for pr
3281553Srgrimes	 *		H -- "host name" of machine where lpr was done
3291553Srgrimes	 *              P -- "person" user's login name
3301553Srgrimes	 *              I -- "indent" amount to indent output
3311553Srgrimes	 *              f -- "file name" name of text file to print
3321553Srgrimes	 *		l -- "file name" text file with control chars
3331553Srgrimes	 *		p -- "file name" text file to print with pr(1)
3341553Srgrimes	 *		t -- "file name" troff(1) file to print
3351553Srgrimes	 *		n -- "file name" ditroff(1) file to print
3361553Srgrimes	 *		d -- "file name" dvi file to print
3371553Srgrimes	 *		g -- "file name" plot(1G) file to print
3381553Srgrimes	 *		v -- "file name" plain raster file to print
3391553Srgrimes	 *		c -- "file name" cifplot file to print
3401553Srgrimes	 *		1 -- "R font file" for troff
3411553Srgrimes	 *		2 -- "I font file" for troff
3421553Srgrimes	 *		3 -- "B font file" for troff
3431553Srgrimes	 *		4 -- "S font file" for troff
3441553Srgrimes	 *		N -- "name" of file (used by lpq)
3451553Srgrimes	 *              U -- "unlink" name of file to remove
3461553Srgrimes	 *                    (after we print it. (Pass 2 only)).
3471553Srgrimes	 *		M -- "mail" to user when done printing
3481553Srgrimes	 *
3491553Srgrimes	 *      getline reads a line and expands tabs to blanks
3501553Srgrimes	 */
3511553Srgrimes
3521553Srgrimes	/* pass 1 */
3531553Srgrimes
3541553Srgrimes	while (getline(cfp))
3551553Srgrimes		switch (line[0]) {
3561553Srgrimes		case 'H':
3571553Srgrimes			strcpy(fromhost, line+1);
3581553Srgrimes			if (class[0] == '\0')
3591553Srgrimes				strncpy(class, line+1, sizeof(class)-1);
3601553Srgrimes			continue;
3611553Srgrimes
3621553Srgrimes		case 'P':
3631553Srgrimes			strncpy(logname, line+1, sizeof(logname)-1);
3641553Srgrimes			if (RS) {			/* restricted */
3651553Srgrimes				if (getpwnam(logname) == NULL) {
3661553Srgrimes					bombed = NOACCT;
3671553Srgrimes					sendmail(line+1, bombed);
3681553Srgrimes					goto pass2;
3691553Srgrimes				}
3701553Srgrimes			}
3711553Srgrimes			continue;
3721553Srgrimes
3731553Srgrimes		case 'S':
3741553Srgrimes			cp = line+1;
3751553Srgrimes			i = 0;
3761553Srgrimes			while (*cp >= '0' && *cp <= '9')
3771553Srgrimes				i = i * 10 + (*cp++ - '0');
3781553Srgrimes			fdev = i;
3791553Srgrimes			cp++;
3801553Srgrimes			i = 0;
3811553Srgrimes			while (*cp >= '0' && *cp <= '9')
3821553Srgrimes				i = i * 10 + (*cp++ - '0');
3831553Srgrimes			fino = i;
3841553Srgrimes			continue;
3851553Srgrimes
3861553Srgrimes		case 'J':
3871553Srgrimes			if (line[1] != '\0')
3881553Srgrimes				strncpy(jobname, line+1, sizeof(jobname)-1);
3891553Srgrimes			else
3901553Srgrimes				strcpy(jobname, " ");
3911553Srgrimes			continue;
3921553Srgrimes
3931553Srgrimes		case 'C':
3941553Srgrimes			if (line[1] != '\0')
3951553Srgrimes				strncpy(class, line+1, sizeof(class)-1);
3961553Srgrimes			else if (class[0] == '\0')
3971553Srgrimes				gethostname(class, sizeof(class));
3981553Srgrimes			continue;
3991553Srgrimes
4001553Srgrimes		case 'T':	/* header title for pr */
4011553Srgrimes			strncpy(title, line+1, sizeof(title)-1);
4021553Srgrimes			continue;
4031553Srgrimes
4041553Srgrimes		case 'L':	/* identification line */
4051553Srgrimes			if (!SH && !HL)
4061553Srgrimes				banner(line+1, jobname);
4071553Srgrimes			continue;
4081553Srgrimes
4091553Srgrimes		case '1':	/* troff fonts */
4101553Srgrimes		case '2':
4111553Srgrimes		case '3':
4121553Srgrimes		case '4':
4131553Srgrimes			if (line[1] != '\0')
4141553Srgrimes				strcpy(fonts[line[0]-'1'], line+1);
4151553Srgrimes			continue;
4161553Srgrimes
4171553Srgrimes		case 'W':	/* page width */
4181553Srgrimes			strncpy(width+2, line+1, sizeof(width)-3);
4191553Srgrimes			continue;
4201553Srgrimes
4211553Srgrimes		case 'I':	/* indent amount */
4221553Srgrimes			strncpy(indent+2, line+1, sizeof(indent)-3);
4231553Srgrimes			continue;
4241553Srgrimes
4251553Srgrimes		default:	/* some file to print */
4261553Srgrimes			switch (i = print(line[0], line+1)) {
4271553Srgrimes			case ERROR:
4281553Srgrimes				if (bombed == OK)
4291553Srgrimes					bombed = FATALERR;
4301553Srgrimes				break;
4311553Srgrimes			case REPRINT:
4321553Srgrimes				(void) fclose(cfp);
4331553Srgrimes				return(REPRINT);
4341553Srgrimes			case FILTERERR:
4351553Srgrimes			case ACCESS:
4361553Srgrimes				bombed = i;
4371553Srgrimes				sendmail(logname, bombed);
4381553Srgrimes			}
4391553Srgrimes			title[0] = '\0';
4401553Srgrimes			continue;
4411553Srgrimes
4421553Srgrimes		case 'N':
4431553Srgrimes		case 'U':
4441553Srgrimes		case 'M':
4451553Srgrimes			continue;
4461553Srgrimes		}
4471553Srgrimes
4481553Srgrimes	/* pass 2 */
4491553Srgrimes
4501553Srgrimespass2:
4511553Srgrimes	fseek(cfp, 0L, 0);
4521553Srgrimes	while (getline(cfp))
4531553Srgrimes		switch (line[0]) {
4541553Srgrimes		case 'L':	/* identification line */
4551553Srgrimes			if (!SH && HL)
4561553Srgrimes				banner(line+1, jobname);
4571553Srgrimes			continue;
4581553Srgrimes
4591553Srgrimes		case 'M':
4601553Srgrimes			if (bombed < NOACCT)	/* already sent if >= NOACCT */
4611553Srgrimes				sendmail(line+1, bombed);
4621553Srgrimes			continue;
4631553Srgrimes
4641553Srgrimes		case 'U':
4651553Srgrimes			(void) unlink(line+1);
4661553Srgrimes		}
4671553Srgrimes	/*
4681553Srgrimes	 * clean-up in case another control file exists
4691553Srgrimes	 */
4701553Srgrimes	(void) fclose(cfp);
4711553Srgrimes	(void) unlink(file);
4721553Srgrimes	return(bombed == OK ? OK : ERROR);
4731553Srgrimes}
4741553Srgrimes
4751553Srgrimes/*
4761553Srgrimes * Print a file.
4771553Srgrimes * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
4781553Srgrimes * Return -1 if a non-recoverable error occured,
4791553Srgrimes * 2 if the filter detected some errors (but printed the job anyway),
4801553Srgrimes * 1 if we should try to reprint this job and
4811553Srgrimes * 0 if all is well.
4821553Srgrimes * Note: all filters take stdin as the file, stdout as the printer,
4831553Srgrimes * stderr as the log file, and must not ignore SIGINT.
4841553Srgrimes */
4851553Srgrimesstatic int
4861553Srgrimesprint(format, file)
4871553Srgrimes	int format;
4881553Srgrimes	char *file;
4891553Srgrimes{
4901553Srgrimes	register int n;
4911553Srgrimes	register char *prog;
4921553Srgrimes	int fi, fo;
4931553Srgrimes	FILE *fp;
4941553Srgrimes	char *av[15], buf[BUFSIZ];
4951553Srgrimes	int pid, p[2], stopped = 0;
4961553Srgrimes	union wait status;
4971553Srgrimes	struct stat stb;
4981553Srgrimes
4991553Srgrimes	if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
5001553Srgrimes		return(ERROR);
5011553Srgrimes	/*
5021553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
5031553Srgrimes	 * still point to the same file or someone is trying to print
5041553Srgrimes	 * something he shouldn't.
5051553Srgrimes	 */
5061553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
5071553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
5081553Srgrimes		return(ACCESS);
5091553Srgrimes	if (!SF && !tof) {		/* start on a fresh page */
5101553Srgrimes		(void) write(ofd, FF, strlen(FF));
5111553Srgrimes		tof = 1;
5121553Srgrimes	}
5131553Srgrimes	if (IF == NULL && (format == 'f' || format == 'l')) {
5141553Srgrimes		tof = 0;
5151553Srgrimes		while ((n = read(fi, buf, BUFSIZ)) > 0)
5161553Srgrimes			if (write(ofd, buf, n) != n) {
5171553Srgrimes				(void) close(fi);
5181553Srgrimes				return(REPRINT);
5191553Srgrimes			}
5201553Srgrimes		(void) close(fi);
5211553Srgrimes		return(OK);
5221553Srgrimes	}
5231553Srgrimes	switch (format) {
5241553Srgrimes	case 'p':	/* print file using 'pr' */
5251553Srgrimes		if (IF == NULL) {	/* use output filter */
5261553Srgrimes			prog = _PATH_PR;
5271553Srgrimes			av[0] = "pr";
5281553Srgrimes			av[1] = width;
5291553Srgrimes			av[2] = length;
5301553Srgrimes			av[3] = "-h";
5311553Srgrimes			av[4] = *title ? title : " ";
5325445Sjoerg			av[5] = "-F";
5335445Sjoerg			av[6] = 0;
5341553Srgrimes			fo = ofd;
5351553Srgrimes			goto start;
5361553Srgrimes		}
5371553Srgrimes		pipe(p);
5381553Srgrimes		if ((prchild = dofork(DORETURN)) == 0) {	/* child */
5391553Srgrimes			dup2(fi, 0);		/* file is stdin */
5401553Srgrimes			dup2(p[1], 1);		/* pipe is stdout */
5411553Srgrimes			for (n = 3; n < NOFILE; n++)
5421553Srgrimes				(void) close(n);
5431553Srgrimes			execl(_PATH_PR, "pr", width, length,
5445445Sjoerg			    "-h", *title ? title : " ", "-F", 0);
5451553Srgrimes			syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
5461553Srgrimes			exit(2);
5471553Srgrimes		}
5481553Srgrimes		(void) close(p[1]);		/* close output side */
5491553Srgrimes		(void) close(fi);
5501553Srgrimes		if (prchild < 0) {
5511553Srgrimes			prchild = 0;
5521553Srgrimes			(void) close(p[0]);
5531553Srgrimes			return(ERROR);
5541553Srgrimes		}
5551553Srgrimes		fi = p[0];			/* use pipe for input */
5561553Srgrimes	case 'f':	/* print plain text file */
5571553Srgrimes		prog = IF;
5581553Srgrimes		av[1] = width;
5591553Srgrimes		av[2] = length;
5601553Srgrimes		av[3] = indent;
5611553Srgrimes		n = 4;
5621553Srgrimes		break;
5631553Srgrimes	case 'l':	/* like 'f' but pass control characters */
5641553Srgrimes		prog = IF;
5651553Srgrimes		av[1] = "-c";
5661553Srgrimes		av[2] = width;
5671553Srgrimes		av[3] = length;
5681553Srgrimes		av[4] = indent;
5691553Srgrimes		n = 5;
5701553Srgrimes		break;
5711553Srgrimes	case 'r':	/* print a fortran text file */
5721553Srgrimes		prog = RF;
5731553Srgrimes		av[1] = width;
5741553Srgrimes		av[2] = length;
5751553Srgrimes		n = 3;
5761553Srgrimes		break;
5771553Srgrimes	case 't':	/* print troff output */
5781553Srgrimes	case 'n':	/* print ditroff output */
5791553Srgrimes	case 'd':	/* print tex output */
5801553Srgrimes		(void) unlink(".railmag");
5811553Srgrimes		if ((fo = creat(".railmag", FILMOD)) < 0) {
5821553Srgrimes			syslog(LOG_ERR, "%s: cannot create .railmag", printer);
5831553Srgrimes			(void) unlink(".railmag");
5841553Srgrimes		} else {
5851553Srgrimes			for (n = 0; n < 4; n++) {
5861553Srgrimes				if (fonts[n][0] != '/')
5871553Srgrimes					(void) write(fo, _PATH_VFONT,
5881553Srgrimes					    sizeof(_PATH_VFONT) - 1);
5891553Srgrimes				(void) write(fo, fonts[n], strlen(fonts[n]));
5901553Srgrimes				(void) write(fo, "\n", 1);
5911553Srgrimes			}
5921553Srgrimes			(void) close(fo);
5931553Srgrimes		}
5941553Srgrimes		prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
5951553Srgrimes		av[1] = pxwidth;
5961553Srgrimes		av[2] = pxlength;
5971553Srgrimes		n = 3;
5981553Srgrimes		break;
5991553Srgrimes	case 'c':	/* print cifplot output */
6001553Srgrimes		prog = CF;
6011553Srgrimes		av[1] = pxwidth;
6021553Srgrimes		av[2] = pxlength;
6031553Srgrimes		n = 3;
6041553Srgrimes		break;
6051553Srgrimes	case 'g':	/* print plot(1G) output */
6061553Srgrimes		prog = GF;
6071553Srgrimes		av[1] = pxwidth;
6081553Srgrimes		av[2] = pxlength;
6091553Srgrimes		n = 3;
6101553Srgrimes		break;
6111553Srgrimes	case 'v':	/* print raster output */
6121553Srgrimes		prog = VF;
6131553Srgrimes		av[1] = pxwidth;
6141553Srgrimes		av[2] = pxlength;
6151553Srgrimes		n = 3;
6161553Srgrimes		break;
6171553Srgrimes	default:
6181553Srgrimes		(void) close(fi);
6191553Srgrimes		syslog(LOG_ERR, "%s: illegal format character '%c'",
6201553Srgrimes			printer, format);
6211553Srgrimes		return(ERROR);
6221553Srgrimes	}
6231553Srgrimes	if ((av[0] = rindex(prog, '/')) != NULL)
6241553Srgrimes		av[0]++;
6251553Srgrimes	else
6261553Srgrimes		av[0] = prog;
6271553Srgrimes	av[n++] = "-n";
6281553Srgrimes	av[n++] = logname;
6291553Srgrimes	av[n++] = "-h";
6301553Srgrimes	av[n++] = fromhost;
6311553Srgrimes	av[n++] = AF;
6321553Srgrimes	av[n] = 0;
6331553Srgrimes	fo = pfd;
6341553Srgrimes	if (ofilter > 0) {		/* stop output filter */
6351553Srgrimes		write(ofd, "\031\1", 2);
6361553Srgrimes		while ((pid =
6371553Srgrimes		    wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
6381553Srgrimes			;
6391553Srgrimes		if (status.w_stopval != WSTOPPED) {
6401553Srgrimes			(void) close(fi);
6411553Srgrimes			syslog(LOG_WARNING, "%s: output filter died (%d)",
6421553Srgrimes				printer, status.w_retcode);
6431553Srgrimes			return(REPRINT);
6441553Srgrimes		}
6451553Srgrimes		stopped++;
6461553Srgrimes	}
6471553Srgrimesstart:
6481553Srgrimes	if ((child = dofork(DORETURN)) == 0) {	/* child */
6491553Srgrimes		dup2(fi, 0);
6501553Srgrimes		dup2(fo, 1);
6511553Srgrimes		n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
6521553Srgrimes		if (n >= 0)
6531553Srgrimes			dup2(n, 2);
6541553Srgrimes		for (n = 3; n < NOFILE; n++)
6551553Srgrimes			(void) close(n);
6561553Srgrimes		execv(prog, av);
6571553Srgrimes		syslog(LOG_ERR, "cannot execv %s", prog);
6581553Srgrimes		exit(2);
6591553Srgrimes	}
6601553Srgrimes	(void) close(fi);
6611553Srgrimes	if (child < 0)
6621553Srgrimes		status.w_retcode = 100;
6631553Srgrimes	else
6641553Srgrimes		while ((pid = wait((int *)&status)) > 0 && pid != child)
6651553Srgrimes			;
6661553Srgrimes	child = 0;
6671553Srgrimes	prchild = 0;
6681553Srgrimes	if (stopped) {		/* restart output filter */
6691553Srgrimes		if (kill(ofilter, SIGCONT) < 0) {
6701553Srgrimes			syslog(LOG_ERR, "cannot restart output filter");
6711553Srgrimes			exit(1);
6721553Srgrimes		}
6731553Srgrimes	}
6741553Srgrimes	tof = 0;
6751553Srgrimes
6761553Srgrimes	/* Copy filter output to "lf" logfile */
6771553Srgrimes	if (fp = fopen(tempfile, "r")) {
6781553Srgrimes		while (fgets(buf, sizeof(buf), fp))
6791553Srgrimes			fputs(buf, stderr);
6801553Srgrimes		fclose(fp);
6811553Srgrimes	}
6821553Srgrimes
6831553Srgrimes	if (!WIFEXITED(status)) {
6841553Srgrimes		syslog(LOG_WARNING, "%s: Daemon filter '%c' terminated (%d)",
6851553Srgrimes			printer, format, status.w_termsig);
6861553Srgrimes		return(ERROR);
6871553Srgrimes	}
6881553Srgrimes	switch (status.w_retcode) {
6891553Srgrimes	case 0:
6901553Srgrimes		tof = 1;
6911553Srgrimes		return(OK);
6921553Srgrimes	case 1:
6931553Srgrimes		return(REPRINT);
6941553Srgrimes	default:
6951553Srgrimes		syslog(LOG_WARNING, "%s: Daemon filter '%c' exited (%d)",
6961553Srgrimes			printer, format, status.w_retcode);
6971553Srgrimes	case 2:
6981553Srgrimes		return(ERROR);
6991553Srgrimes	}
7001553Srgrimes}
7011553Srgrimes
7021553Srgrimes/*
7031553Srgrimes * Send the daemon control file (cf) and any data files.
7041553Srgrimes * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
7051553Srgrimes * 0 if all is well.
7061553Srgrimes */
7071553Srgrimesstatic int
7081553Srgrimessendit(file)
7091553Srgrimes	char *file;
7101553Srgrimes{
7111553Srgrimes	register int i, err = OK;
7121553Srgrimes	char *cp, last[BUFSIZ];
7131553Srgrimes
7141553Srgrimes	/*
7151553Srgrimes	 * open control file
7161553Srgrimes	 */
7171553Srgrimes	if ((cfp = fopen(file, "r")) == NULL)
7181553Srgrimes		return(OK);
7191553Srgrimes	/*
7201553Srgrimes	 *      read the control file for work to do
7211553Srgrimes	 *
7221553Srgrimes	 *      file format -- first character in the line is a command
7231553Srgrimes	 *      rest of the line is the argument.
7241553Srgrimes	 *      commands of interest are:
7251553Srgrimes	 *
7261553Srgrimes	 *            a-z -- "file name" name of file to print
7271553Srgrimes	 *              U -- "unlink" name of file to remove
7281553Srgrimes	 *                    (after we print it. (Pass 2 only)).
7291553Srgrimes	 */
7301553Srgrimes
7311553Srgrimes	/*
7321553Srgrimes	 * pass 1
7331553Srgrimes	 */
7341553Srgrimes	while (getline(cfp)) {
7351553Srgrimes	again:
7361553Srgrimes		if (line[0] == 'S') {
7371553Srgrimes			cp = line+1;
7381553Srgrimes			i = 0;
7391553Srgrimes			while (*cp >= '0' && *cp <= '9')
7401553Srgrimes				i = i * 10 + (*cp++ - '0');
7411553Srgrimes			fdev = i;
7421553Srgrimes			cp++;
7431553Srgrimes			i = 0;
7441553Srgrimes			while (*cp >= '0' && *cp <= '9')
7451553Srgrimes				i = i * 10 + (*cp++ - '0');
7461553Srgrimes			fino = i;
7471553Srgrimes			continue;
7481553Srgrimes		}
7491553Srgrimes		if (line[0] >= 'a' && line[0] <= 'z') {
7501553Srgrimes			strcpy(last, line);
7511553Srgrimes			while (i = getline(cfp))
7521553Srgrimes				if (strcmp(last, line))
7531553Srgrimes					break;
7541553Srgrimes			switch (sendfile('\3', last+1)) {
7551553Srgrimes			case OK:
7561553Srgrimes				if (i)
7571553Srgrimes					goto again;
7581553Srgrimes				break;
7591553Srgrimes			case REPRINT:
7601553Srgrimes				(void) fclose(cfp);
7611553Srgrimes				return(REPRINT);
7621553Srgrimes			case ACCESS:
7631553Srgrimes				sendmail(logname, ACCESS);
7641553Srgrimes			case ERROR:
7651553Srgrimes				err = ERROR;
7661553Srgrimes			}
7671553Srgrimes			break;
7681553Srgrimes		}
7691553Srgrimes	}
7701553Srgrimes	if (err == OK && sendfile('\2', file) > 0) {
7711553Srgrimes		(void) fclose(cfp);
7721553Srgrimes		return(REPRINT);
7731553Srgrimes	}
7741553Srgrimes	/*
7751553Srgrimes	 * pass 2
7761553Srgrimes	 */
7771553Srgrimes	fseek(cfp, 0L, 0);
7781553Srgrimes	while (getline(cfp))
7791553Srgrimes		if (line[0] == 'U')
7801553Srgrimes			(void) unlink(line+1);
7811553Srgrimes	/*
7821553Srgrimes	 * clean-up in case another control file exists
7831553Srgrimes	 */
7841553Srgrimes	(void) fclose(cfp);
7851553Srgrimes	(void) unlink(file);
7861553Srgrimes	return(err);
7871553Srgrimes}
7881553Srgrimes
7891553Srgrimes/*
7901553Srgrimes * Send a data file to the remote machine and spool it.
7911553Srgrimes * Return positive if we should try resending.
7921553Srgrimes */
7931553Srgrimesstatic int
7941553Srgrimessendfile(type, file)
7951553Srgrimes	int type;
7961553Srgrimes	char *file;
7971553Srgrimes{
7981553Srgrimes	register int f, i, amt;
7991553Srgrimes	struct stat stb;
8001553Srgrimes	char buf[BUFSIZ];
8011553Srgrimes	int sizerr, resp;
8021553Srgrimes
8031553Srgrimes	if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
8041553Srgrimes		return(ERROR);
8051553Srgrimes	/*
8061553Srgrimes	 * Check to see if data file is a symbolic link. If so, it should
8071553Srgrimes	 * still point to the same file or someone is trying to print something
8081553Srgrimes	 * he shouldn't.
8091553Srgrimes	 */
8101553Srgrimes	if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
8111553Srgrimes	    (stb.st_dev != fdev || stb.st_ino != fino))
8121553Srgrimes		return(ACCESS);
8131553Srgrimes	(void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
8141553Srgrimes	amt = strlen(buf);
8151553Srgrimes	for (i = 0;  ; i++) {
8161553Srgrimes		if (write(pfd, buf, amt) != amt ||
8171553Srgrimes		    (resp = response()) < 0 || resp == '\1') {
8181553Srgrimes			(void) close(f);
8191553Srgrimes			return(REPRINT);
8201553Srgrimes		} else if (resp == '\0')
8211553Srgrimes			break;
8221553Srgrimes		if (i == 0)
8231553Srgrimes			pstatus("no space on remote; waiting for queue to drain");
8241553Srgrimes		if (i == 10)
8251553Srgrimes			syslog(LOG_ALERT, "%s: can't send to %s; queue full",
8261553Srgrimes				printer, RM);
8271553Srgrimes		sleep(5 * 60);
8281553Srgrimes	}
8291553Srgrimes	if (i)
8301553Srgrimes		pstatus("sending to %s", RM);
8311553Srgrimes	sizerr = 0;
8321553Srgrimes	for (i = 0; i < stb.st_size; i += BUFSIZ) {
8331553Srgrimes		amt = BUFSIZ;
8341553Srgrimes		if (i + amt > stb.st_size)
8351553Srgrimes			amt = stb.st_size - i;
8361553Srgrimes		if (sizerr == 0 && read(f, buf, amt) != amt)
8371553Srgrimes			sizerr = 1;
8381553Srgrimes		if (write(pfd, buf, amt) != amt) {
8391553Srgrimes			(void) close(f);
8401553Srgrimes			return(REPRINT);
8411553Srgrimes		}
8421553Srgrimes	}
8431553Srgrimes
8441553Srgrimes
8451553Srgrimes
8461553Srgrimes
8471553Srgrimes	(void) close(f);
8481553Srgrimes	if (sizerr) {
8491553Srgrimes		syslog(LOG_INFO, "%s: %s: changed size", printer, file);
8501553Srgrimes		/* tell recvjob to ignore this file */
8511553Srgrimes		(void) write(pfd, "\1", 1);
8521553Srgrimes		return(ERROR);
8531553Srgrimes	}
8541553Srgrimes	if (write(pfd, "", 1) != 1 || response())
8551553Srgrimes		return(REPRINT);
8561553Srgrimes	return(OK);
8571553Srgrimes}
8581553Srgrimes
8591553Srgrimes/*
8601553Srgrimes * Check to make sure there have been no errors and that both programs
8611553Srgrimes * are in sync with eachother.
8621553Srgrimes * Return non-zero if the connection was lost.
8631553Srgrimes */
8641553Srgrimesstatic char
8651553Srgrimesresponse()
8661553Srgrimes{
8671553Srgrimes	char resp;
8681553Srgrimes
8691553Srgrimes	if (read(pfd, &resp, 1) != 1) {
8701553Srgrimes		syslog(LOG_INFO, "%s: lost connection", printer);
8711553Srgrimes		return(-1);
8721553Srgrimes	}
8731553Srgrimes	return(resp);
8741553Srgrimes}
8751553Srgrimes
8761553Srgrimes/*
8771553Srgrimes * Banner printing stuff
8781553Srgrimes */
8791553Srgrimesstatic void
8801553Srgrimesbanner(name1, name2)
8811553Srgrimes	char *name1, *name2;
8821553Srgrimes{
8831553Srgrimes	time_t tvec;
8841553Srgrimes	extern char *ctime();
8851553Srgrimes
8861553Srgrimes	time(&tvec);
8871553Srgrimes	if (!SF && !tof)
8881553Srgrimes		(void) write(ofd, FF, strlen(FF));
8891553Srgrimes	if (SB) {	/* short banner only */
8901553Srgrimes		if (class[0]) {
8911553Srgrimes			(void) write(ofd, class, strlen(class));
8921553Srgrimes			(void) write(ofd, ":", 1);
8931553Srgrimes		}
8941553Srgrimes		(void) write(ofd, name1, strlen(name1));
8951553Srgrimes		(void) write(ofd, "  Job: ", 7);
8961553Srgrimes		(void) write(ofd, name2, strlen(name2));
8971553Srgrimes		(void) write(ofd, "  Date: ", 8);
8981553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
8991553Srgrimes		(void) write(ofd, "\n", 1);
9001553Srgrimes	} else {	/* normal banner */
9011553Srgrimes		(void) write(ofd, "\n\n\n", 3);
9021553Srgrimes		scan_out(ofd, name1, '\0');
9031553Srgrimes		(void) write(ofd, "\n\n", 2);
9041553Srgrimes		scan_out(ofd, name2, '\0');
9051553Srgrimes		if (class[0]) {
9061553Srgrimes			(void) write(ofd,"\n\n\n",3);
9071553Srgrimes			scan_out(ofd, class, '\0');
9081553Srgrimes		}
9091553Srgrimes		(void) write(ofd, "\n\n\n\n\t\t\t\t\tJob:  ", 15);
9101553Srgrimes		(void) write(ofd, name2, strlen(name2));
9111553Srgrimes		(void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
9121553Srgrimes		(void) write(ofd, ctime(&tvec), 24);
9131553Srgrimes		(void) write(ofd, "\n", 1);
9141553Srgrimes	}
9151553Srgrimes	if (!SF)
9161553Srgrimes		(void) write(ofd, FF, strlen(FF));
9171553Srgrimes	tof = 1;
9181553Srgrimes}
9191553Srgrimes
9201553Srgrimesstatic char *
9211553Srgrimesscnline(key, p, c)
9221553Srgrimes	register int key;
9231553Srgrimes	register char *p;
9241553Srgrimes	int c;
9251553Srgrimes{
9261553Srgrimes	register scnwidth;
9271553Srgrimes
9281553Srgrimes	for (scnwidth = WIDTH; --scnwidth;) {
9291553Srgrimes		key <<= 1;
9301553Srgrimes		*p++ = key & 0200 ? c : BACKGND;
9311553Srgrimes	}
9321553Srgrimes	return (p);
9331553Srgrimes}
9341553Srgrimes
9351553Srgrimes#define TRC(q)	(((q)-' ')&0177)
9361553Srgrimes
9371553Srgrimesstatic void
9381553Srgrimesscan_out(scfd, scsp, dlm)
9391553Srgrimes	int scfd, dlm;
9401553Srgrimes	char *scsp;
9411553Srgrimes{
9421553Srgrimes	register char *strp;
9431553Srgrimes	register nchrs, j;
9441553Srgrimes	char outbuf[LINELEN+1], *sp, c, cc;
9451553Srgrimes	int d, scnhgt;
9461553Srgrimes	extern char scnkey[][HEIGHT];	/* in lpdchar.c */
9471553Srgrimes
9481553Srgrimes	for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
9491553Srgrimes		strp = &outbuf[0];
9501553Srgrimes		sp = scsp;
9511553Srgrimes		for (nchrs = 0; ; ) {
9521553Srgrimes			d = dropit(c = TRC(cc = *sp++));
9531553Srgrimes			if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
9541553Srgrimes				for (j = WIDTH; --j;)
9551553Srgrimes					*strp++ = BACKGND;
9561553Srgrimes			else
9571553Srgrimes				strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
9581553Srgrimes			if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
9591553Srgrimes				break;
9601553Srgrimes			*strp++ = BACKGND;
9611553Srgrimes			*strp++ = BACKGND;
9621553Srgrimes		}
9631553Srgrimes		while (*--strp == BACKGND && strp >= outbuf)
9641553Srgrimes			;
9651553Srgrimes		strp++;
9661553Srgrimes		*strp++ = '\n';
9671553Srgrimes		(void) write(scfd, outbuf, strp-outbuf);
9681553Srgrimes	}
9691553Srgrimes}
9701553Srgrimes
9711553Srgrimesstatic int
9721553Srgrimesdropit(c)
9731553Srgrimes	int c;
9741553Srgrimes{
9751553Srgrimes	switch(c) {
9761553Srgrimes
9771553Srgrimes	case TRC('_'):
9781553Srgrimes	case TRC(';'):
9791553Srgrimes	case TRC(','):
9801553Srgrimes	case TRC('g'):
9811553Srgrimes	case TRC('j'):
9821553Srgrimes	case TRC('p'):
9831553Srgrimes	case TRC('q'):
9841553Srgrimes	case TRC('y'):
9851553Srgrimes		return (DROP);
9861553Srgrimes
9871553Srgrimes	default:
9881553Srgrimes		return (0);
9891553Srgrimes	}
9901553Srgrimes}
9911553Srgrimes
9921553Srgrimes/*
9931553Srgrimes * sendmail ---
9941553Srgrimes *   tell people about job completion
9951553Srgrimes */
9961553Srgrimesstatic void
9971553Srgrimessendmail(user, bombed)
9981553Srgrimes	char *user;
9991553Srgrimes	int bombed;
10001553Srgrimes{
10011553Srgrimes	register int i;
10021553Srgrimes	int p[2], s;
10031553Srgrimes	register char *cp;
10041553Srgrimes	char buf[100];
10051553Srgrimes	struct stat stb;
10061553Srgrimes	FILE *fp;
10071553Srgrimes
10081553Srgrimes	pipe(p);
10091553Srgrimes	if ((s = dofork(DORETURN)) == 0) {		/* child */
10101553Srgrimes		dup2(p[0], 0);
10111553Srgrimes		for (i = 3; i < NOFILE; i++)
10121553Srgrimes			(void) close(i);
10131553Srgrimes		if ((cp = rindex(_PATH_SENDMAIL, '/')) != NULL)
10141553Srgrimes			cp++;
10151553Srgrimes	else
10161553Srgrimes			cp = _PATH_SENDMAIL;
10171553Srgrimes		sprintf(buf, "%s@%s", user, fromhost);
10181553Srgrimes		execl(_PATH_SENDMAIL, cp, buf, 0);
10191553Srgrimes		exit(0);
10201553Srgrimes	} else if (s > 0) {				/* parent */
10211553Srgrimes		dup2(p[1], 1);
10221553Srgrimes		printf("To: %s@%s\n", user, fromhost);
10231553Srgrimes		printf("Subject: printer job\n\n");
10241553Srgrimes		printf("Your printer job ");
10251553Srgrimes		if (*jobname)
10261553Srgrimes			printf("(%s) ", jobname);
10271553Srgrimes		switch (bombed) {
10281553Srgrimes		case OK:
10291553Srgrimes			printf("\ncompleted successfully\n");
10301553Srgrimes			break;
10311553Srgrimes		default:
10321553Srgrimes		case FATALERR:
10331553Srgrimes			printf("\ncould not be printed\n");
10341553Srgrimes			break;
10351553Srgrimes		case NOACCT:
10361553Srgrimes			printf("\ncould not be printed without an account on %s\n", host);
10371553Srgrimes			break;
10381553Srgrimes		case FILTERERR:
10391553Srgrimes			if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
10401553Srgrimes			    (fp = fopen(tempfile, "r")) == NULL) {
10411553Srgrimes				printf("\nwas printed but had some errors\n");
10421553Srgrimes				break;
10431553Srgrimes			}
10441553Srgrimes			printf("\nwas printed but had the following errors:\n");
10451553Srgrimes			while ((i = getc(fp)) != EOF)
10461553Srgrimes				putchar(i);
10471553Srgrimes			(void) fclose(fp);
10481553Srgrimes			break;
10491553Srgrimes		case ACCESS:
10501553Srgrimes			printf("\nwas not printed because it was not linked to the original file\n");
10511553Srgrimes		}
10521553Srgrimes		fflush(stdout);
10531553Srgrimes		(void) close(1);
10541553Srgrimes	}
10551553Srgrimes	(void) close(p[0]);
10561553Srgrimes	(void) close(p[1]);
10571553Srgrimes	wait(&s);
10581553Srgrimes}
10591553Srgrimes
10601553Srgrimes/*
10611553Srgrimes * dofork - fork with retries on failure
10621553Srgrimes */
10631553Srgrimesstatic int
10641553Srgrimesdofork(action)
10651553Srgrimes	int action;
10661553Srgrimes{
10671553Srgrimes	register int i, pid;
10681553Srgrimes
10691553Srgrimes	for (i = 0; i < 20; i++) {
10701553Srgrimes		if ((pid = fork()) < 0) {
10711553Srgrimes			sleep((unsigned)(i*i));
10721553Srgrimes			continue;
10731553Srgrimes		}
10741553Srgrimes		/*
10751553Srgrimes		 * Child should run as daemon instead of root
10761553Srgrimes		 */
10771553Srgrimes		if (pid == 0)
10781553Srgrimes			setuid(DU);
10791553Srgrimes		return(pid);
10801553Srgrimes	}
10811553Srgrimes	syslog(LOG_ERR, "can't fork");
10821553Srgrimes
10831553Srgrimes	switch (action) {
10841553Srgrimes	case DORETURN:
10851553Srgrimes		return (-1);
10861553Srgrimes	default:
10871553Srgrimes		syslog(LOG_ERR, "bad action (%d) to dofork", action);
10881553Srgrimes		/*FALL THRU*/
10891553Srgrimes	case DOABORT:
10901553Srgrimes		exit(1);
10911553Srgrimes	}
10921553Srgrimes	/*NOTREACHED*/
10931553Srgrimes}
10941553Srgrimes
10951553Srgrimes/*
10961553Srgrimes * Kill child processes to abort current job.
10971553Srgrimes */
10981553Srgrimesstatic void
10991553Srgrimesabortpr(signo)
11001553Srgrimes	int signo;
11011553Srgrimes{
11021553Srgrimes	(void) unlink(tempfile);
11031553Srgrimes	kill(0, SIGINT);
11041553Srgrimes	if (ofilter > 0)
11051553Srgrimes		kill(ofilter, SIGCONT);
11061553Srgrimes	while (wait(NULL) > 0)
11071553Srgrimes		;
11081553Srgrimes	exit(0);
11091553Srgrimes}
11101553Srgrimes
11111553Srgrimesstatic void
11121553Srgrimesinit()
11131553Srgrimes{
11141553Srgrimes	int status;
11151553Srgrimes	char *s;
11161553Srgrimes
11171553Srgrimes	if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
11181553Srgrimes		syslog(LOG_ERR, "can't open printer description file");
11191553Srgrimes		exit(1);
11201553Srgrimes	} else if (status == -1) {
11211553Srgrimes		syslog(LOG_ERR, "unknown printer: %s", printer);
11221553Srgrimes		exit(1);
11231553Srgrimes	} else if (status == -3)
11241553Srgrimes		fatal("potential reference loop detected in printcap file");
11251553Srgrimes
11261553Srgrimes	if (cgetstr(bp, "lp", &LP) == -1)
11271553Srgrimes		LP = _PATH_DEFDEVLP;
11281553Srgrimes	if (cgetstr(bp, "rp", &RP) == -1)
11291553Srgrimes		RP = DEFLP;
11301553Srgrimes	if (cgetstr(bp, "lo", &LO) == -1)
11311553Srgrimes		LO = DEFLOCK;
11321553Srgrimes	if (cgetstr(bp, "st", &ST) == -1)
11331553Srgrimes		ST = DEFSTAT;
11341553Srgrimes	if (cgetstr(bp, "lf", &LF) == -1)
11351553Srgrimes		LF = _PATH_CONSOLE;
11361553Srgrimes	if (cgetstr(bp, "sd", &SD) == -1)
11371553Srgrimes		SD = _PATH_DEFSPOOL;
11381553Srgrimes	if (cgetnum(bp, "du", &DU) < 0)
11391553Srgrimes		DU = DEFUID;
11401553Srgrimes	if (cgetstr(bp,"ff", &FF) == -1)
11411553Srgrimes		FF = DEFFF;
11421553Srgrimes	if (cgetnum(bp, "pw", &PW) < 0)
11431553Srgrimes		PW = DEFWIDTH;
11441553Srgrimes	sprintf(&width[2], "%d", PW);
11451553Srgrimes	if (cgetnum(bp, "pl", &PL) < 0)
11461553Srgrimes		PL = DEFLENGTH;
11471553Srgrimes	sprintf(&length[2], "%d", PL);
11481553Srgrimes	if (cgetnum(bp,"px", &PX) < 0)
11491553Srgrimes		PX = 0;
11501553Srgrimes	sprintf(&pxwidth[2], "%d", PX);
11511553Srgrimes	if (cgetnum(bp, "py", &PY) < 0)
11521553Srgrimes		PY = 0;
11531553Srgrimes	sprintf(&pxlength[2], "%d", PY);
11541553Srgrimes	cgetstr(bp, "rm", &RM);
11551553Srgrimes	if (s = checkremote())
11561553Srgrimes		syslog(LOG_WARNING, s);
11571553Srgrimes
11581553Srgrimes	cgetstr(bp, "af", &AF);
11591553Srgrimes	cgetstr(bp, "of", &OF);
11601553Srgrimes	cgetstr(bp, "if", &IF);
11611553Srgrimes	cgetstr(bp, "rf", &RF);
11621553Srgrimes	cgetstr(bp, "tf", &TF);
11631553Srgrimes	cgetstr(bp, "nf", &NF);
11641553Srgrimes	cgetstr(bp, "df", &DF);
11651553Srgrimes	cgetstr(bp, "gf", &GF);
11661553Srgrimes	cgetstr(bp, "vf", &VF);
11671553Srgrimes	cgetstr(bp, "cf", &CF);
11681553Srgrimes	cgetstr(bp, "tr", &TR);
11691553Srgrimes
11701553Srgrimes	RS = (cgetcap(bp, "rs", ':') != NULL);
11711553Srgrimes	SF = (cgetcap(bp, "sf", ':') != NULL);
11721553Srgrimes	SH = (cgetcap(bp, "sh", ':') != NULL);
11731553Srgrimes	SB = (cgetcap(bp, "sb", ':') != NULL);
11741553Srgrimes	HL = (cgetcap(bp, "hl", ':') != NULL);
11751553Srgrimes	RW = (cgetcap(bp, "rw", ':') != NULL);
11761553Srgrimes
11771553Srgrimes	cgetnum(bp, "br", &BR);
11781553Srgrimes	if (cgetnum(bp, "fc", &FC) < 0)
11791553Srgrimes		FC = 0;
11801553Srgrimes	if (cgetnum(bp, "fs", &FS) < 0)
11811553Srgrimes		FS = 0;
11821553Srgrimes	if (cgetnum(bp, "xc", &XC) < 0)
11831553Srgrimes		XC = 0;
11841553Srgrimes	if (cgetnum(bp, "xs", &XS) < 0)
11851553Srgrimes		XS = 0;
11861553Srgrimes
11871553Srgrimes	tof = (cgetcap(bp, "fo", ':') == NULL);
11881553Srgrimes}
11891553Srgrimes
11901553Srgrimes/*
11911553Srgrimes * Acquire line printer or remote connection.
11921553Srgrimes */
11931553Srgrimesstatic void
11941553Srgrimesopenpr()
11951553Srgrimes{
11961553Srgrimes	register int i, n;
11971553Srgrimes	int resp;
11981553Srgrimes
11991553Srgrimes	if (!sendtorem && *LP) {
12001553Srgrimes		for (i = 1; ; i = i < 32 ? i << 1 : i) {
12011553Srgrimes			pfd = open(LP, RW ? O_RDWR : O_WRONLY);
12021553Srgrimes			if (pfd >= 0)
12031553Srgrimes				break;
12041553Srgrimes			if (errno == ENOENT) {
12051553Srgrimes				syslog(LOG_ERR, "%s: %m", LP);
12061553Srgrimes				exit(1);
12071553Srgrimes			}
12081553Srgrimes			if (i == 1)
12091553Srgrimes				pstatus("waiting for %s to become ready (offline ?)", printer);
12101553Srgrimes			sleep(i);
12111553Srgrimes		}
12121553Srgrimes		if (isatty(pfd))
12131553Srgrimes			setty();
12141553Srgrimes		pstatus("%s is ready and printing", printer);
12151553Srgrimes	} else if (RM != NULL) {
12161553Srgrimes		for (i = 1; ; i = i < 256 ? i << 1 : i) {
12171553Srgrimes			resp = -1;
12181553Srgrimes			pfd = getport(RM);
12191553Srgrimes			if (pfd >= 0) {
12201553Srgrimes				(void) sprintf(line, "\2%s\n", RP);
12211553Srgrimes				n = strlen(line);
12221553Srgrimes				if (write(pfd, line, n) == n &&
12231553Srgrimes				    (resp = response()) == '\0')
12241553Srgrimes					break;
12251553Srgrimes				(void) close(pfd);
12261553Srgrimes			}
12271553Srgrimes			if (i == 1) {
12281553Srgrimes				if (resp < 0)
12291553Srgrimes					pstatus("waiting for %s to come up", RM);
12301553Srgrimes				else {
12311553Srgrimes					pstatus("waiting for queue to be enabled on %s", RM);
12321553Srgrimes					i = 256;
12331553Srgrimes				}
12341553Srgrimes			}
12351553Srgrimes			sleep(i);
12361553Srgrimes		}
12371553Srgrimes		pstatus("sending to %s", RM);
12381553Srgrimes		remote = 1;
12391553Srgrimes	} else {
12401553Srgrimes		syslog(LOG_ERR, "%s: no line printer device or host name",
12411553Srgrimes			printer);
12421553Srgrimes		exit(1);
12431553Srgrimes	}
12441553Srgrimes	/*
12451553Srgrimes	 * Start up an output filter, if needed.
12461553Srgrimes	 */
12471553Srgrimes	if (!remote && OF) {
12481553Srgrimes		int p[2];
12491553Srgrimes		char *cp;
12501553Srgrimes
12511553Srgrimes		pipe(p);
12521553Srgrimes		if ((ofilter = dofork(DOABORT)) == 0) {	/* child */
12531553Srgrimes			dup2(p[0], 0);		/* pipe is std in */
12541553Srgrimes			dup2(pfd, 1);		/* printer is std out */
12551553Srgrimes			for (i = 3; i < NOFILE; i++)
12561553Srgrimes				(void) close(i);
12571553Srgrimes			if ((cp = rindex(OF, '/')) == NULL)
12581553Srgrimes				cp = OF;
12591553Srgrimes			else
12601553Srgrimes				cp++;
12611553Srgrimes			execl(OF, cp, width, length, 0);
12621553Srgrimes			syslog(LOG_ERR, "%s: %s: %m", printer, OF);
12631553Srgrimes			exit(1);
12641553Srgrimes		}
12651553Srgrimes		(void) close(p[0]);		/* close input side */
12661553Srgrimes		ofd = p[1];			/* use pipe for output */
12671553Srgrimes	} else {
12681553Srgrimes		ofd = pfd;
12691553Srgrimes		ofilter = 0;
12701553Srgrimes	}
12711553Srgrimes}
12721553Srgrimes
12731553Srgrimesstruct bauds {
12741553Srgrimes	int	baud;
12751553Srgrimes	int	speed;
12761553Srgrimes} bauds[] = {
12771553Srgrimes	50,	B50,
12781553Srgrimes	75,	B75,
12791553Srgrimes	110,	B110,
12801553Srgrimes	134,	B134,
12811553Srgrimes	150,	B150,
12821553Srgrimes	200,	B200,
12831553Srgrimes	300,	B300,
12841553Srgrimes	600,	B600,
12851553Srgrimes	1200,	B1200,
12861553Srgrimes	1800,	B1800,
12871553Srgrimes	2400,	B2400,
12881553Srgrimes	4800,	B4800,
12891553Srgrimes	9600,	B9600,
12901553Srgrimes	19200,	EXTA,
12911553Srgrimes	38400,	EXTB,
12921553Srgrimes	0,	0
12931553Srgrimes};
12941553Srgrimes
12951553Srgrimes/*
12961553Srgrimes * setup tty lines.
12971553Srgrimes */
12981553Srgrimesstatic void
12991553Srgrimessetty()
13001553Srgrimes{
13011553Srgrimes	struct sgttyb ttybuf;
13021553Srgrimes	register struct bauds *bp;
13031553Srgrimes
13041553Srgrimes	if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
13051553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
13061553Srgrimes		exit(1);
13071553Srgrimes	}
13081553Srgrimes	if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
13091553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCGETP): %m", printer);
13101553Srgrimes		exit(1);
13111553Srgrimes	}
13121553Srgrimes	if (BR > 0) {
13131553Srgrimes		for (bp = bauds; bp->baud; bp++)
13141553Srgrimes			if (BR == bp->baud)
13151553Srgrimes				break;
13161553Srgrimes		if (!bp->baud) {
13171553Srgrimes			syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
13181553Srgrimes			exit(1);
13191553Srgrimes		}
13201553Srgrimes		ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
13211553Srgrimes	}
13221553Srgrimes	ttybuf.sg_flags &= ~FC;
13231553Srgrimes	ttybuf.sg_flags |= FS;
13241553Srgrimes	if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
13251553Srgrimes		syslog(LOG_ERR, "%s: ioctl(TIOCSETP): %m", printer);
13261553Srgrimes		exit(1);
13271553Srgrimes	}
13281553Srgrimes	if (XC) {
13291553Srgrimes		if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
13301553Srgrimes			syslog(LOG_ERR, "%s: ioctl(TIOCLBIC): %m", printer);
13311553Srgrimes			exit(1);
13321553Srgrimes		}
13331553Srgrimes	}
13341553Srgrimes	if (XS) {
13351553Srgrimes		if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
13361553Srgrimes			syslog(LOG_ERR, "%s: ioctl(TIOCLBIS): %m", printer);
13371553Srgrimes			exit(1);
13381553Srgrimes		}
13391553Srgrimes	}
13401553Srgrimes}
13411553Srgrimes
13421553Srgrimes#if __STDC__
13431553Srgrimes#include <stdarg.h>
13441553Srgrimes#else
13451553Srgrimes#include <varargs.h>
13461553Srgrimes#endif
13471553Srgrimes
13481553Srgrimesvoid
13491553Srgrimes#if __STDC__
13501553Srgrimespstatus(const char *msg, ...)
13511553Srgrimes#else
13521553Srgrimespstatus(msg, va_alist)
13531553Srgrimes	char *msg;
13541553Srgrimes        va_dcl
13551553Srgrimes#endif
13561553Srgrimes{
13571553Srgrimes	register int fd;
13581553Srgrimes	char buf[BUFSIZ];
13591553Srgrimes	va_list ap;
13601553Srgrimes#if __STDC__
13611553Srgrimes	va_start(ap, msg);
13621553Srgrimes#else
13631553Srgrimes	va_start(ap);
13641553Srgrimes#endif
13651553Srgrimes
13661553Srgrimes	umask(0);
13671553Srgrimes	fd = open(ST, O_WRONLY|O_CREAT, 0664);
13681553Srgrimes	if (fd < 0 || flock(fd, LOCK_EX) < 0) {
13691553Srgrimes		syslog(LOG_ERR, "%s: %s: %m", printer, ST);
13701553Srgrimes		exit(1);
13711553Srgrimes	}
13721553Srgrimes	ftruncate(fd, 0);
13731553Srgrimes	(void)vsnprintf(buf, sizeof(buf), msg, ap);
13741553Srgrimes	va_end(ap);
13751553Srgrimes	strcat(buf, "\n");
13761553Srgrimes	(void) write(fd, buf, strlen(buf));
13771553Srgrimes	(void) close(fd);
13781553Srgrimes}
1379