xref: /csrg-svn/local/transcript/src/pscomm.c (revision 33262)
1*33262Ssklower #ifndef lint
2*33262Ssklower static char Notice[] = "Copyright (c) 1985 Adobe Systems Incorporated";
3*33262Ssklower static char *RCSID="$Header: pscomm.bsd,v 2.1 85/11/24 11:50:16 shore Rel $";
4*33262Ssklower #endif
5*33262Ssklower /* pscomm.c
6*33262Ssklower  *
7*33262Ssklower  * Copyright (C) 1985 Adobe Systems Incorporated
8*33262Ssklower  *
9*33262Ssklower  * 4.2BSD lpr/lpd communications filter for PostScript printers
10*33262Ssklower  * (formerly "psif" in TranScript release 1.0)
11*33262Ssklower  *
12*33262Ssklower  * pscomm is the general communications filter for
13*33262Ssklower  * sending files to a PostScript printer (e.g., an Apple LaserWriter,
14*33262Ssklower  * QMS PostScript printer, or Linotype PostScript typesetter)
15*33262Ssklower  * via RS232 lines.  It does page accounting, error handling/reporting,
16*33262Ssklower  * job logging, banner page printing, etc.
17*33262Ssklower  * It observes (parts of) the PostScript file structuring conventions.
18*33262Ssklower  * In particular, it distinguishes between PostScript files (beginning
19*33262Ssklower  * with the "%!" magic number) -- which are shipped to the printer --
20*33262Ssklower  * and text files (no magic number) which are formatted and listed
21*33262Ssklower  * on the printer.  Files which begin with "%!PS-Adobe-" may be
22*33262Ssklower  * page-reversed if the target printer has that option specified.
23*33262Ssklower  *
24*33262Ssklower  * depending on the values of BANNERFIRST and BANNERLAST,
25*33262Ssklower  * pscomm looks for a file named ".banner", (created by the "of" filter)
26*33262Ssklower  * in the current working directory and ships it to the printer also.
27*33262Ssklower  *
28*33262Ssklower  * pscomm gets called with:
29*33262Ssklower  *	stdin	== the file to print (may be a pipe!)
30*33262Ssklower  *	stdout	== the printer
31*33262Ssklower  *	stderr	== the printer log file
32*33262Ssklower  *	cwd	== the spool directory
33*33262Ssklower  *	argv	== set up by interface shell script:
34*33262Ssklower  *	  filtername	-P printer
35*33262Ssklower  *			-p filtername
36*33262Ssklower  *			[-r]		(don't ever reverse)
37*33262Ssklower  *			-n login
38*33262Ssklower  *			-h host
39*33262Ssklower  *			[accntfile]
40*33262Ssklower  *
41*33262Ssklower  *	environ	== various environment variable effect behavior
42*33262Ssklower  *		VERBOSELOG	- do verbose log file output
43*33262Ssklower  *		BANNERFIRST	- print .banner before job
44*33262Ssklower  *		BANNERLAST	- print .banner after job
45*33262Ssklower  *		REVERSE		- page reversal filter program
46*33262Ssklower  *				  (no reversal if null or missing)
47*33262Ssklower  *		PSLIBDIR	- transcript library directory
48*33262Ssklower  *		PSTEXT		- simple text formatting filter
49*33262Ssklower  *		JOBOUTPUT	- file for actual printer stream
50*33262Ssklower  *				  output (if defined)
51*33262Ssklower  *
52*33262Ssklower  * pscomm depends on certain additional features of the 4.2BSD spooling
53*33262Ssklower  * architecture.  In particular it assumes that the printer status file
54*33262Ssklower  * has the default name (./status) and it uses this file to communicate
55*33262Ssklower  * printer error status information to the user -- the contents of the
56*33262Ssklower  * status file gets incorporated in "lpq" and "lpc status" messages.
57*33262Ssklower  *
58*33262Ssklower  * Edit History:
59*33262Ssklower  * Andrew Shore: Sat Nov 16 11:59:58 1985
60*33262Ssklower  * End Edit History.
61*33262Ssklower  *
62*33262Ssklower  * RCSLOG:
63*33262Ssklower  * $Log:	pscomm.bsd,v $
64*33262Ssklower  * Revision 2.1  85/11/24  11:50:16  shore
65*33262Ssklower  * Product Release 2.0
66*33262Ssklower  *
67*33262Ssklower  * Revision 1.1  85/11/20  00:35:21  shore
68*33262Ssklower  * Initial revision
69*33262Ssklower  *
70*33262Ssklower  * Revision 1.2  85/05/14  11:25:29  shore
71*33262Ssklower  * better support for BANNERLAST, still buggy though
72*33262Ssklower  *
73*33262Ssklower  *
74*33262Ssklower  */
75*33262Ssklower 
76*33262Ssklower #include <ctype.h>
77*33262Ssklower #include <setjmp.h>
78*33262Ssklower #include <sgtty.h>
79*33262Ssklower #include <signal.h>
80*33262Ssklower #include <stdio.h>
81*33262Ssklower #include <strings.h>
82*33262Ssklower 
83*33262Ssklower #include <sys/file.h>
84*33262Ssklower #include <sys/ioctl.h>
85*33262Ssklower #include <sys/time.h>
86*33262Ssklower #include <sys/resource.h>
87*33262Ssklower #include <sys/wait.h>
88*33262Ssklower #include <sys/types.h>
89*33262Ssklower #include <sys/stat.h>
90*33262Ssklower 
91*33262Ssklower #include "transcript.h"
92*33262Ssklower #include "psspool.h"
93*33262Ssklower 
94*33262Ssklower #ifdef BDEBUG
95*33262Ssklower #define debugp(x) {fprintf x ; (void) fflush(stderr);}
96*33262Ssklower #else
97*33262Ssklower #define debugp(x)
98*33262Ssklower #endif BDEBUG
99*33262Ssklower 
100*33262Ssklower /*
101*33262Ssklower  * the following string is sent to the printer when we want it to
102*33262Ssklower  * report its current pagecount (for accounting)
103*33262Ssklower  */
104*33262Ssklower 
105*33262Ssklower private char *getpages = "\n(%%%%[ pagecount: )print \
106*33262Ssklower statusdict/pagecount get exec(                )cvs print( ]%%%%)= flush\n%s";
107*33262Ssklower 
108*33262Ssklower private jmp_buf waitonreverse, startstatus, dwait, sendint;
109*33262Ssklower 
110*33262Ssklower private char	*prog;			/* invoking program name */
111*33262Ssklower private char	*name;			/* user login name */
112*33262Ssklower private char	*host;			/* host name */
113*33262Ssklower private char	*pname;			/* printer name */
114*33262Ssklower private char	*accountingfile;	/* file for printer accounting */
115*33262Ssklower private int	doactng;		/* true if we can do accounting */
116*33262Ssklower private int	progress, oldprogress;	/* finite progress counts */
117*33262Ssklower private int	getstatus = FALSE;
118*33262Ssklower private int	revdone = FALSE;	/* reverse done, send new */
119*33262Ssklower private int	goahead = FALSE;	/* got initial status back */
120*33262Ssklower private int	gotemt = FALSE;		/* got ^D ack from listener */
121*33262Ssklower private int	sendend = TRUE;		/* send an ^D */
122*33262Ssklower 
123*33262Ssklower private char *bannerfirst;
124*33262Ssklower private char *bannerlast;
125*33262Ssklower private char *verboselog;
126*33262Ssklower private char *reverse;
127*33262Ssklower private int BannerFirst;
128*33262Ssklower private int BannerLast;
129*33262Ssklower private int VerboseLog;
130*33262Ssklower 
131*33262Ssklower private int	fpid = 0;	/* formatter pid */
132*33262Ssklower private int	cpid = 0;	/* listener pid */
133*33262Ssklower 
134*33262Ssklower private int	intrup = FALSE;	/* interrupt flag */
135*33262Ssklower 
136*33262Ssklower private char abortbuf[] = "\003";	/* ^C abort */
137*33262Ssklower private char statusbuf[] = "\024";	/* ^T status */
138*33262Ssklower private char eofbuf[] = "\004";		/* ^D end of file */
139*33262Ssklower 
140*33262Ssklower private char EOFerr[] = "%s: unexpected EOF from printer (%s)!\n";
141*33262Ssklower 
142*33262Ssklower /* global file descriptors (avoid stdio buffering!) */
143*33262Ssklower private int fdsend;		/* to printer (from stdout) */
144*33262Ssklower private int fdlisten;		/* from printer (same tty line) */
145*33262Ssklower private int fdinput;		/* file to print (from stdin) */
146*33262Ssklower 
147*33262Ssklower private FILE *jobout;		/* special printer output log */
148*33262Ssklower 
149*33262Ssklower private int flg = FREAD|FWRITE;	 /* ioctl FLUSH arg */
150*33262Ssklower 
151*33262Ssklower 
152*33262Ssklower extern char *getenv();
153*33262Ssklower 
154*33262Ssklower private VOID	intinit();
155*33262Ssklower private VOID	intsend();
156*33262Ssklower private VOID	intwait();
157*33262Ssklower private VOID	salarm();
158*33262Ssklower private VOID	walarm();
159*33262Ssklower private VOID	falarm();
160*33262Ssklower private VOID	reverseready();
161*33262Ssklower private VOID	readynow();
162*33262Ssklower private VOID	emtdead();
163*33262Ssklower private VOID	emtdone();
164*33262Ssklower private char 	*FindPattern();
165*33262Ssklower 
166*33262Ssklower #define SENDALARM 90
167*33262Ssklower #define WAITALARM 30
168*33262Ssklower 
169*33262Ssklower main(argc,argv)
170*33262Ssklower 	int argc;
171*33262Ssklower 	char *argv[];
172*33262Ssklower {
173*33262Ssklower     register char  *cp;
174*33262Ssklower     register int cnt, wc;
175*33262Ssklower     register char *mbp;
176*33262Ssklower 
177*33262Ssklower     char  **av;
178*33262Ssklower     long clock;		/* for log timestamp */
179*33262Ssklower     char magic[11];	/* first few bytes of stdin ?magic number and type */
180*33262Ssklower     int  noReverse = 0; /* flag if we should never page reverse */
181*33262Ssklower     int  canReverse = 0;/* flag if we can page-reverse the ps file */
182*33262Ssklower     int  reversing = 0;
183*33262Ssklower     FILE *streamin;
184*33262Ssklower 
185*33262Ssklower     char mybuf[BUFSIZ];
186*33262Ssklower     int wpid;
187*33262Ssklower     union wait status;
188*33262Ssklower     int fdpipe[2];
189*33262Ssklower     int format = 0;
190*33262Ssklower     int i;
191*33262Ssklower 
192*33262Ssklower     VOIDC signal(SIGINT, intinit);
193*33262Ssklower     VOIDC signal(SIGHUP, intinit);
194*33262Ssklower     VOIDC signal(SIGQUIT, intinit);
195*33262Ssklower     VOIDC signal(SIGTERM, intinit);
196*33262Ssklower 
197*33262Ssklower     /* parse command-line arguments */
198*33262Ssklower     /* the argv (see header comments) comes from the spooler daemon */
199*33262Ssklower     /* itself, so it should be canonical, but at least one 4.2-based */
200*33262Ssklower     /* system uses -nlogin -hhost (insead of -n login -h host) so I */
201*33262Ssklower     /* check for both */
202*33262Ssklower 
203*33262Ssklower     av = argv;
204*33262Ssklower     prog = *av;
205*33262Ssklower 
206*33262Ssklower     while (--argc) {
207*33262Ssklower 	if (*(cp = *++av) == '-') {
208*33262Ssklower 	    switch (*(cp + 1)) {
209*33262Ssklower 		case 'P':	/* printer name */
210*33262Ssklower 		    argc--;
211*33262Ssklower 		    pname = *(++av);
212*33262Ssklower 		    break;
213*33262Ssklower 
214*33262Ssklower 		case 'n': 	/* user name */
215*33262Ssklower 		    argc--;
216*33262Ssklower 		    name = *(++av);
217*33262Ssklower 		    break;
218*33262Ssklower 
219*33262Ssklower 		case 'h': 	/* host */
220*33262Ssklower 		    argc--;
221*33262Ssklower 		    host = *(++av);
222*33262Ssklower 		    break;
223*33262Ssklower 
224*33262Ssklower 		case 'p':	/* prog */
225*33262Ssklower 		    argc--;
226*33262Ssklower 		    prog = *(++av);
227*33262Ssklower 		    break;
228*33262Ssklower 
229*33262Ssklower 		case 'r':	/* never reverse */
230*33262Ssklower 		    argc--;
231*33262Ssklower 		    noReverse = 1;
232*33262Ssklower 		    break;
233*33262Ssklower 
234*33262Ssklower 		default:	/* unknown */
235*33262Ssklower 		    fprintf(stderr,"%s: unknown option: %s\n",prog,cp);
236*33262Ssklower 		    break;
237*33262Ssklower 	    }
238*33262Ssklower 	}
239*33262Ssklower 	else
240*33262Ssklower 	    accountingfile = cp;
241*33262Ssklower     }
242*33262Ssklower 
243*33262Ssklower     debugp((stderr,"args: %s %s %s %s\n",prog,host,name,accountingfile));
244*33262Ssklower 
245*33262Ssklower     /* do printer-specific options processing */
246*33262Ssklower 
247*33262Ssklower     VerboseLog = 1;
248*33262Ssklower     BannerFirst = BannerLast = 0;
249*33262Ssklower     reverse = NULL;
250*33262Ssklower     if (bannerfirst=envget("BANNERFIRST")) {
251*33262Ssklower 	BannerFirst=atoi(bannerfirst);
252*33262Ssklower     }
253*33262Ssklower     if (bannerlast=envget("BANNERLAST")) {
254*33262Ssklower 	BannerLast=atoi(bannerlast);
255*33262Ssklower     }
256*33262Ssklower     if (verboselog=envget("VERBOSELOG")) {
257*33262Ssklower 	VerboseLog=atoi(verboselog);
258*33262Ssklower     }
259*33262Ssklower     if (!noReverse) {
260*33262Ssklower 	reverse=envget("REVERSE");	/* name of the filter itself */
261*33262Ssklower     }
262*33262Ssklower 
263*33262Ssklower     if (VerboseLog) {
264*33262Ssklower 	fprintf(stderr, "%s: %s:%s %s start - %s", prog, host, name, pname,
265*33262Ssklower             (VOIDC time(&clock), ctime(&clock)));
266*33262Ssklower 	VOIDC fflush(stderr);
267*33262Ssklower     }
268*33262Ssklower     debugp((stderr,"%s: pid %d ppid %d\n",prog,getpid(),getppid()));
269*33262Ssklower     debugp((stderr,"%s: options BF %d BL %d VL %d R %s\n",prog,BannerFirst,
270*33262Ssklower     	BannerLast, VerboseLog, ((reverse == NULL) ? "norev": reverse)));
271*33262Ssklower 
272*33262Ssklower     /* IMPORTANT: in the case of cascaded filters, */
273*33262Ssklower     /* stdin may be a pipe! (and hence we cannot seek!) */
274*33262Ssklower 
275*33262Ssklower     if ((cnt = read(fileno(stdin),magic,11)) != 11) goto badfile;
276*33262Ssklower     debugp((stderr,"%s: magic number is %11.11s\n",prog,magic));
277*33262Ssklower     streamin = stdin;
278*33262Ssklower 
279*33262Ssklower     if (strncmp(magic,"%!PS-Adobe-",11) == 0) {
280*33262Ssklower 	canReverse = TRUE;
281*33262Ssklower 	goto go_ahead;
282*33262Ssklower     }
283*33262Ssklower     else if (strncmp(magic,"%!",2) == 0) {
284*33262Ssklower 	canReverse = FALSE;
285*33262Ssklower 	goto go_ahead;
286*33262Ssklower     }
287*33262Ssklower 
288*33262Ssklower     /* here is where you might test for other file type
289*33262Ssklower      * e.g., PRESS, imPRESS, DVI, Mac-generated, etc.
290*33262Ssklower      */
291*33262Ssklower 
292*33262Ssklower     /* final sanity check on the text file, to guard
293*33262Ssklower      * against arbitrary binary data
294*33262Ssklower      */
295*33262Ssklower 
296*33262Ssklower     for (i = 0; i < 11; i++) {
297*33262Ssklower 	if (!isascii(magic[i]) || (!isprint(magic[i]) && !isspace(magic[i]))){
298*33262Ssklower 	    fprintf(stderr,"%s: spooled binary file rejected\n",prog);
299*33262Ssklower 	    VOIDC fflush(stderr);
300*33262Ssklower 	    sprintf(mybuf,"%s/bogusmsg.ps",envget("PSLIBDIR"));
301*33262Ssklower 	    if ((streamin = freopen(mybuf,"r",stdin)) == NULL) {
302*33262Ssklower 		exit(THROW_AWAY);
303*33262Ssklower 	    }
304*33262Ssklower 	    format = 1;
305*33262Ssklower 	    goto lastchance;
306*33262Ssklower 	}
307*33262Ssklower     }
308*33262Ssklower 
309*33262Ssklower     goto format_text;
310*33262Ssklower 
311*33262Ssklower     badfile:
312*33262Ssklower         fprintf(stderr,"%s: bad magic number, EOF\n", prog);
313*33262Ssklower 	VOIDC fflush(stderr);
314*33262Ssklower 	exit(THROW_AWAY);
315*33262Ssklower 
316*33262Ssklower     format_text:
317*33262Ssklower         /* exec dumb formatter to make a listing */
318*33262Ssklower 	    debugp((stderr,"formatting\n"));
319*33262Ssklower 	    format = 1;
320*33262Ssklower 	    VOIDC lseek(0,0L,0);
321*33262Ssklower 	    rewind(stdin);
322*33262Ssklower 	    if (pipe (fdpipe)) pexit2(prog, "format pipe",THROW_AWAY);
323*33262Ssklower 	    if ((fpid = fork()) < 0) pexit2(prog, "format fork",THROW_AWAY);
324*33262Ssklower 	    if (fpid == 0) { /* child */
325*33262Ssklower 		/* set up child stdout to feed parent stdin */
326*33262Ssklower 		if (close(1) || (dup(fdpipe[1]) != 1)
327*33262Ssklower 		|| close(fdpipe[1]) || close(fdpipe[0])) {
328*33262Ssklower 		    pexit2(prog, "format child",THROW_AWAY);
329*33262Ssklower 		}
330*33262Ssklower 		execl(envget("PSTEXT"), "pstext", pname, 0);
331*33262Ssklower 	   	pexit2(prog,"format exec",THROW_AWAY);
332*33262Ssklower 	    }
333*33262Ssklower 	/* parent continues */
334*33262Ssklower 	/* set up stdin to be pipe */
335*33262Ssklower 	if (close(0) || (dup(fdpipe[0]) != 0)
336*33262Ssklower 	|| close(fdpipe[0]) || close(fdpipe[1])) {
337*33262Ssklower 	    pexit2(prog, "format parent",THROW_AWAY);
338*33262Ssklower 	}
339*33262Ssklower 
340*33262Ssklower 	/* fall through to spooler with new stdin */
341*33262Ssklower 	/* can't seek here but we should be at the right place */
342*33262Ssklower 	streamin = fdopen(0,"r");
343*33262Ssklower 	canReverse = TRUE; /* we know we can reverse pstext output */
344*33262Ssklower 
345*33262Ssklower     go_ahead:
346*33262Ssklower 
347*33262Ssklower     /* do page reversal if specified */
348*33262Ssklower     if (reversing = ((reverse != NULL) && canReverse)) {
349*33262Ssklower 	debugp((stderr,"reversing\n"));
350*33262Ssklower 	VOIDC setjmp(waitonreverse);
351*33262Ssklower 	if (!revdone) {
352*33262Ssklower 	    VOIDC signal(SIGEMT, reverseready);
353*33262Ssklower 	    if (pipe (fdpipe)) pexit2(prog, "reverse pipe", THROW_AWAY);
354*33262Ssklower 	    if ((fpid = fork()) < 0) pexit2(prog, "reverse fork", THROW_AWAY);
355*33262Ssklower 	    if (fpid == 0) { /* child */
356*33262Ssklower 		/* set up child stdout to feed parent stdin */
357*33262Ssklower 		if (close(1) || (dup(fdpipe[1]) != 1)
358*33262Ssklower 		|| close(fdpipe[1]) || close(fdpipe[0])) {
359*33262Ssklower 		    pexit2(prog, "reverse child", THROW_AWAY);
360*33262Ssklower 		}
361*33262Ssklower 		execl(reverse, "psrv", pname, 0);
362*33262Ssklower 		pexit2(prog,"reverse exec",THROW_AWAY);
363*33262Ssklower 	    }
364*33262Ssklower 	    /* parent continues */
365*33262Ssklower 	    if (close(0) || (dup(fdpipe[0]) != 0)
366*33262Ssklower 	    || close(fdpipe[0]) || close(fdpipe[1])) {
367*33262Ssklower 		pexit2(prog, "reverse parent", THROW_AWAY);
368*33262Ssklower 	    }
369*33262Ssklower 	    /* fall through to spooler with new stdin */
370*33262Ssklower 	    /* VOIDC lseek(0,0L,0); */
371*33262Ssklower 	    streamin = fdopen(0,"r");
372*33262Ssklower 
373*33262Ssklower 	    while (TRUE) {
374*33262Ssklower 		if (revdone) break;
375*33262Ssklower 		pause();
376*33262Ssklower 	    }
377*33262Ssklower 	}
378*33262Ssklower 	VOIDC signal(SIGEMT, SIG_IGN);
379*33262Ssklower 	debugp((stderr,"%s: reverse feeding\n",prog));
380*33262Ssklower     }
381*33262Ssklower 
382*33262Ssklower     lastchance:;
383*33262Ssklower 
384*33262Ssklower     /* establish an input stream from the printer --
385*33262Ssklower      * the printcap entry specifies "rw" and we get
386*33262Ssklower      * invoked with stdout == the device, so we
387*33262Ssklower      * dup stdout, and reopen it for reading;
388*33262Ssklower      * this seems to work fine...
389*33262Ssklower      */
390*33262Ssklower 
391*33262Ssklower     fdinput = fileno(streamin); /* the file to print */
392*33262Ssklower     fdsend = fileno(stdout);	/* the printer (write) */
393*33262Ssklower 
394*33262Ssklower     if ((fdlisten = dup(fdsend)) < 0) /* the printer (read) */
395*33262Ssklower        pexit(prog, THROW_AWAY);
396*33262Ssklower 
397*33262Ssklower     doactng = name && accountingfile && (access(accountingfile, W_OK) == 0);
398*33262Ssklower 
399*33262Ssklower     /* get control of the "status" message file.
400*33262Ssklower      * we copy the current one to ".status" so we can restore it
401*33262Ssklower      * on exit (to be clean).
402*33262Ssklower      * Our ability to use this is publicized nowhere in the
403*33262Ssklower      * 4.2 lpr documentation, so things might go bad for us.
404*33262Ssklower      * We will use it to report that printer errors condition
405*33262Ssklower      * has been detected, and the printer should be checked.
406*33262Ssklower      * Unfortunately, this notice may persist through
407*33262Ssklower      * the end of the print job, but this is no big deal.
408*33262Ssklower      */
409*33262Ssklower     BackupStatus(".status","status");
410*33262Ssklower 
411*33262Ssklower     if ((cpid = fork()) < 0) pexit(prog, THROW_AWAY);
412*33262Ssklower     else if (cpid) {/* parent - sender */
413*33262Ssklower 	VOIDC setjmp(sendint);
414*33262Ssklower 
415*33262Ssklower 	if (intrup) {
416*33262Ssklower 	    /* we only get here if there was an interrupt */
417*33262Ssklower 
418*33262Ssklower 	    fprintf(stderr,"%s: abort (sending)\n",prog);
419*33262Ssklower 	    VOIDC fflush(stderr);
420*33262Ssklower 
421*33262Ssklower 	    /* flush and restart output to printer,
422*33262Ssklower 	     * send an abort (^C) request and wait for the job to end
423*33262Ssklower 	     */
424*33262Ssklower 	    if (ioctl(fdsend, TIOCFLUSH,&flg) || ioctl(fdsend, TIOCSTART,&flg)
425*33262Ssklower 	    || (write(fdsend, abortbuf, 1) != 1)) {
426*33262Ssklower 		RestoreStatus();
427*33262Ssklower 		pexit(prog,THROW_AWAY);
428*33262Ssklower 	    }
429*33262Ssklower 	    debugp((stderr,"%s: sent interrupt - waiting\n",prog));
430*33262Ssklower 	    intrup = 0;
431*33262Ssklower 	    goto donefile; /* sorry ewd! */
432*33262Ssklower 	}
433*33262Ssklower 
434*33262Ssklower         VOIDC signal(SIGINT, intsend);
435*33262Ssklower         VOIDC signal(SIGHUP, intsend);
436*33262Ssklower         VOIDC signal(SIGQUIT, intsend);
437*33262Ssklower         VOIDC signal(SIGTERM, intsend);
438*33262Ssklower 	VOIDC signal(SIGEMT, readynow);
439*33262Ssklower 
440*33262Ssklower 	progress = oldprogress = 0; /* finite progress on sender */
441*33262Ssklower 	getstatus = FALSE; /* prime the pump for fun FALSE; */
442*33262Ssklower 
443*33262Ssklower 	VOIDC signal(SIGALRM, salarm); /* sending phase alarm */
444*33262Ssklower 	VOIDC alarm(SENDALARM); /* schedule an alarm/timeout */
445*33262Ssklower 
446*33262Ssklower 	/* loop, trying to send a ^T to get printer status
447*33262Ssklower 	 * We will hang here (and post a message) if the printer
448*33262Ssklower 	 * is unreachable.  Eventually, we will succeed, the listener
449*33262Ssklower 	 * will see the status report, signal us, and we will proceed
450*33262Ssklower 	 */
451*33262Ssklower 
452*33262Ssklower 	cnt = 1;
453*33262Ssklower 	VOIDC setjmp(startstatus);
454*33262Ssklower 
455*33262Ssklower 	while (TRUE) {
456*33262Ssklower 	    if (goahead) break;
457*33262Ssklower 	    debugp((stderr,"%s: get start status\n",prog));
458*33262Ssklower 	    VOIDC write(fdsend, statusbuf, 1);
459*33262Ssklower 	    pause();
460*33262Ssklower 	    if (goahead) break;
461*33262Ssklower 	    /* if we get here, we got an alarm */
462*33262Ssklower 	    ioctl(fdsend, TIOCFLUSH, &flg);
463*33262Ssklower 	    ioctl(fdsend, TIOCSTART, &flg);
464*33262Ssklower 	    sprintf(mybuf, "Not Responding for %d minutes",
465*33262Ssklower 	    	(cnt * SENDALARM+30)/60);
466*33262Ssklower 	    Status(mybuf);
467*33262Ssklower 	    alarm(SENDALARM);
468*33262Ssklower 	    cnt++;
469*33262Ssklower 	}
470*33262Ssklower 
471*33262Ssklower 	VOIDC signal(SIGEMT, emtdead); /* now EMTs mean printer died */
472*33262Ssklower 
473*33262Ssklower 	RestoreStatus();
474*33262Ssklower 	debugp((stderr,"%s: printer responding\n",prog));
475*33262Ssklower 
476*33262Ssklower 	/* initial page accounting (BEFORE break page) */
477*33262Ssklower 	if (doactng) {
478*33262Ssklower 	    sprintf(mybuf, getpages, "\004");
479*33262Ssklower 	    VOIDC write(fdsend, mybuf, strlen(mybuf));
480*33262Ssklower 	    progress++;
481*33262Ssklower         }
482*33262Ssklower 
483*33262Ssklower 	/* initial break page ? */
484*33262Ssklower 	if (BannerFirst) {
485*33262Ssklower 	    SendBanner();
486*33262Ssklower 	    progress++;
487*33262Ssklower 	}
488*33262Ssklower 
489*33262Ssklower 	/* ship the magic number! */
490*33262Ssklower 	if ((!format) && (!reversing)) {
491*33262Ssklower 	   VOIDC write(fdsend,magic,11);
492*33262Ssklower 	   progress++;
493*33262Ssklower 	}
494*33262Ssklower 
495*33262Ssklower 	/* now ship the rest of the file */
496*33262Ssklower 
497*33262Ssklower 	VOIDC alarm(SENDALARM); /* schedule an alarm */
498*33262Ssklower 
499*33262Ssklower 	while ((cnt = read(fdinput, mybuf, sizeof mybuf)) > 0) {
500*33262Ssklower 	    /* VOIDC alarm(SENDALARM);	/* we made progress, reset alarm */
501*33262Ssklower 	    if (intrup == TRUE) break;
502*33262Ssklower 
503*33262Ssklower 	    /* get status every other time */
504*33262Ssklower 	    if (getstatus) {
505*33262Ssklower 		VOIDC write(fdsend, statusbuf, 1);
506*33262Ssklower 		getstatus = FALSE;
507*33262Ssklower 		progress++;
508*33262Ssklower 	    }
509*33262Ssklower 	    mbp = mybuf;
510*33262Ssklower 	    while ((cnt > 0) && ((wc = write(fdsend, mbp, cnt)) != cnt)) {
511*33262Ssklower 		/* this seems necessary but not sure why */
512*33262Ssklower 		if (wc < 0) {
513*33262Ssklower 		    fprintf(stderr,"%s: error writing to printer:\n",prog);
514*33262Ssklower 		    perror(prog);
515*33262Ssklower 		    RestoreStatus();
516*33262Ssklower 		    sleep(10);
517*33262Ssklower 		    exit(TRY_AGAIN);
518*33262Ssklower 		}
519*33262Ssklower 		mbp += wc;
520*33262Ssklower 		cnt -= wc;
521*33262Ssklower 		progress++;
522*33262Ssklower 	    }
523*33262Ssklower 	    progress++;
524*33262Ssklower 	}
525*33262Ssklower 	if (cnt < 0) {
526*33262Ssklower 	    fprintf(stderr,"%s: error reading from stdin: \n", prog);
527*33262Ssklower 	    perror(prog);
528*33262Ssklower 	    RestoreStatus();
529*33262Ssklower 	    sleep(10);
530*33262Ssklower 	    exit(TRY_AGAIN);	/* kill the listener? */
531*33262Ssklower 	}
532*33262Ssklower 
533*33262Ssklower 
534*33262Ssklower 	donefile:;
535*33262Ssklower 
536*33262Ssklower 	sendend = 1;
537*33262Ssklower 
538*33262Ssklower 	VOIDC setjmp(dwait);
539*33262Ssklower 
540*33262Ssklower 	if (sendend && !gotemt) {
541*33262Ssklower 
542*33262Ssklower 	    VOIDC signal(SIGEMT, emtdone);
543*33262Ssklower 
544*33262Ssklower 	    debugp((stderr,"%s: done sending\n",prog));
545*33262Ssklower 
546*33262Ssklower 	    /* now send the PostScript EOF character */
547*33262Ssklower 	    VOIDC write(fdsend, eofbuf, 1);
548*33262Ssklower 	    sendend = 0;
549*33262Ssklower 	    progress++;
550*33262Ssklower 
551*33262Ssklower 	    VOIDC signal(SIGINT, intwait);
552*33262Ssklower 	    VOIDC signal(SIGHUP, intwait);
553*33262Ssklower 	    VOIDC signal(SIGQUIT, intwait);
554*33262Ssklower 	    VOIDC signal(SIGTERM, intwait);
555*33262Ssklower 
556*33262Ssklower 	    VOIDC signal(SIGALRM, walarm);
557*33262Ssklower 	    VOIDC alarm(WAITALARM);
558*33262Ssklower 	    getstatus = TRUE;
559*33262Ssklower 	}
560*33262Ssklower 
561*33262Ssklower 	/* wait to sync with listener EMT signal
562*33262Ssklower 	 * to indicate it got an EOF from the printer
563*33262Ssklower 	 */
564*33262Ssklower 	while (TRUE) {
565*33262Ssklower 	    if (gotemt) break;
566*33262Ssklower 	    if (getstatus) {
567*33262Ssklower 		VOIDC write(fdsend, statusbuf, 1);
568*33262Ssklower 		getstatus = FALSE;
569*33262Ssklower 	    }
570*33262Ssklower 	    debugp((stderr,"waiting e%d i%d %d %d\n",
571*33262Ssklower 	    	gotemt,intrup,wpid,status));
572*33262Ssklower 	    wpid = wait(&status);
573*33262Ssklower 	    if (wpid == -1) break;
574*33262Ssklower 	}
575*33262Ssklower 
576*33262Ssklower 	VOIDC signal(SIGALRM, falarm);
577*33262Ssklower 	VOIDC alarm(WAITALARM);
578*33262Ssklower 
579*33262Ssklower 	/* final break page ? */
580*33262Ssklower 	if (BannerLast) {
581*33262Ssklower 	    SendBanner();
582*33262Ssklower 	    progress++;
583*33262Ssklower 	}
584*33262Ssklower 	if (BannerFirst) VOIDC unlink(".banner");
585*33262Ssklower 
586*33262Ssklower 	/* final page accounting */
587*33262Ssklower 	if (doactng) {
588*33262Ssklower 	    sprintf(mybuf, getpages, "");
589*33262Ssklower 	    VOIDC write(fdsend, mybuf, strlen(mybuf));
590*33262Ssklower 	    progress++;
591*33262Ssklower         }
592*33262Ssklower 	/* if we sent anything, finish it off */
593*33262Ssklower 	if (BannerLast || doactng) {
594*33262Ssklower 	    VOIDC write(fdsend, eofbuf, 1);
595*33262Ssklower 	    progress++;
596*33262Ssklower 	}
597*33262Ssklower 
598*33262Ssklower 	/* wait for listener to die */
599*33262Ssklower 	VOIDC setjmp(dwait);
600*33262Ssklower         while ((wpid = wait(&status)) > 0);
601*33262Ssklower 	VOIDC alarm(0);
602*33262Ssklower 	VOIDC signal(SIGINT, SIG_IGN);
603*33262Ssklower 	VOIDC signal(SIGHUP, SIG_IGN);
604*33262Ssklower 	VOIDC signal(SIGQUIT, SIG_IGN);
605*33262Ssklower 	VOIDC signal(SIGTERM, SIG_IGN);
606*33262Ssklower 	VOIDC signal(SIGEMT, SIG_IGN);
607*33262Ssklower 	debugp((stderr,"w2: s%lo p%d = p%d\n", status, wpid, cpid));
608*33262Ssklower 
609*33262Ssklower 	if (VerboseLog) {
610*33262Ssklower 	    fprintf(stderr,"%s: end - %s",prog,
611*33262Ssklower 	    	(VOIDC time(&clock),ctime(&clock)));
612*33262Ssklower 	    VOIDC fflush(stderr);
613*33262Ssklower 	}
614*33262Ssklower     RestoreStatus();
615*33262Ssklower     exit(0);
616*33262Ssklower     }
617*33262Ssklower     else {/* child - listener */
618*33262Ssklower       register FILE *psin;
619*33262Ssklower       register int r;
620*33262Ssklower 
621*33262Ssklower       char pbuf[BUFSIZ]; /* buffer for pagecount info */
622*33262Ssklower       char *pb;		/* pointer for above */
623*33262Ssklower       int pc1, pc2; 	/* page counts before and after job */
624*33262Ssklower       int sc;		/* pattern match count for sscanf */
625*33262Ssklower       char *outname;	/* file name for job output */
626*33262Ssklower       int havejobout = FALSE; /* flag if jobout != stderr */
627*33262Ssklower       int ppid;		/* parent process id */
628*33262Ssklower 
629*33262Ssklower       VOIDC signal(SIGINT, SIG_IGN);
630*33262Ssklower       VOIDC signal(SIGHUP, SIG_IGN);
631*33262Ssklower       VOIDC signal(SIGQUIT, SIG_IGN);
632*33262Ssklower       VOIDC signal(SIGTERM, SIG_IGN);
633*33262Ssklower       VOIDC signal(SIGALRM, SIG_IGN);
634*33262Ssklower 
635*33262Ssklower       ppid = getppid();
636*33262Ssklower 
637*33262Ssklower       /* get jobout from environment if there, otherwise use stderr */
638*33262Ssklower       if (((outname = envget("JOBOUTPUT")) == NULL)
639*33262Ssklower       || ((jobout = fopen(outname,"w")) == NULL)) {
640*33262Ssklower 	  jobout = stderr;
641*33262Ssklower       }
642*33262Ssklower       else havejobout = TRUE;
643*33262Ssklower 
644*33262Ssklower       pc1 = pc2 = -1; /* bogus initial values */
645*33262Ssklower       if ((psin = fdopen(fdlisten, "r")) == NULL) {
646*33262Ssklower 	  RestoreStatus();
647*33262Ssklower 	  pexit(prog, THROW_AWAY);
648*33262Ssklower       }
649*33262Ssklower 
650*33262Ssklower       /* listen for first status (idle?) */
651*33262Ssklower       pb = pbuf;
652*33262Ssklower       *pb = '\0';
653*33262Ssklower       while (TRUE) {
654*33262Ssklower 	  r = getc(psin);
655*33262Ssklower 	  if (r == EOF) {
656*33262Ssklower 	      fprintf(stderr, EOFerr, prog, "startup");
657*33262Ssklower 	      VOIDC fflush(stderr);
658*33262Ssklower 	      sleep(20); /* printer may be coming up */
659*33262Ssklower 	      /* RestoreStatus(); */
660*33262Ssklower 	      /* exit(TRY_AGAIN); */
661*33262Ssklower 	  }
662*33262Ssklower 	  if ((r & 0377) == '\n') break; /* newline */
663*33262Ssklower 	  *pb++ = r;
664*33262Ssklower       }
665*33262Ssklower       *pb = 0;
666*33262Ssklower       if (strcmp(pbuf, "%%[ status: idle ]%%\r") != 0) {
667*33262Ssklower 	  fprintf(stderr,"%s: initial status - %s\n",prog,pbuf);
668*33262Ssklower 	  VOIDC fflush(stderr);
669*33262Ssklower       }
670*33262Ssklower 
671*33262Ssklower       /* flush input state and signal sender that we heard something */
672*33262Ssklower       ioctl(fdlisten, TIOCFLUSH, &flg);
673*33262Ssklower 
674*33262Ssklower       VOIDC kill(ppid,SIGEMT);
675*33262Ssklower 
676*33262Ssklower       /* listen for first pagecount */
677*33262Ssklower       if (doactng) {
678*33262Ssklower         pb = pbuf;
679*33262Ssklower 	*pb = '\0';
680*33262Ssklower 	while (TRUE) {
681*33262Ssklower 	  r = getc(psin);
682*33262Ssklower 	  if (r == EOF) {
683*33262Ssklower 	      fprintf(stderr, EOFerr, prog, "accounting1");
684*33262Ssklower 	      VOIDC fflush(stderr);
685*33262Ssklower 	      RestoreStatus();
686*33262Ssklower 	      sleep(10);	/* give interface a chance */
687*33262Ssklower 	      exit(TRY_AGAIN);
688*33262Ssklower 	  }
689*33262Ssklower 	  if ((r&0377) == 004) break; /* PS_EOF */
690*33262Ssklower 	  *pb++ = r;
691*33262Ssklower 	}
692*33262Ssklower 	*pb = '\0';
693*33262Ssklower 
694*33262Ssklower 	if (pb = FindPattern(pb, pbuf, "%%[ pagecount: ")) {
695*33262Ssklower 	    sc = sscanf(pb, "%%%%[ pagecount: %d ]%%%%\r", &pc1);
696*33262Ssklower 	}
697*33262Ssklower 	if ((pb == NULL) || (sc != 1)) {
698*33262Ssklower 	    fprintf(stderr, "%s: accounting error 1 (%s)\n", prog,pbuf);
699*33262Ssklower 	    VOIDC fflush(stderr);
700*33262Ssklower 	}
701*33262Ssklower 	debugp((stderr,"%s: accounting 1 (%s)\n",prog,pbuf));
702*33262Ssklower       }
703*33262Ssklower 
704*33262Ssklower       /* listen for the user job */
705*33262Ssklower       while (TRUE) {
706*33262Ssklower 	r = getc(psin);
707*33262Ssklower 	if ((r&0377) == 004) break; /* PS_EOF */
708*33262Ssklower 	else if (r == EOF) {
709*33262Ssklower 	    VOIDC fclose(psin);
710*33262Ssklower 	    fprintf(stderr, EOFerr, prog, "job");
711*33262Ssklower 	    VOIDC fflush(stderr);
712*33262Ssklower 	    RestoreStatus();
713*33262Ssklower 	    VOIDC kill(ppid,SIGEMT);
714*33262Ssklower 	    exit(THROW_AWAY);
715*33262Ssklower 	}
716*33262Ssklower 	GotChar(r);
717*33262Ssklower       }
718*33262Ssklower 
719*33262Ssklower       /* let sender know we saw the end of the job */
720*33262Ssklower       /* sync - wait for sender to restart us */
721*33262Ssklower 
722*33262Ssklower       debugp((stderr,"%s: listener saw eof, signaling\n",prog));
723*33262Ssklower 
724*33262Ssklower       VOIDC kill(ppid,SIGEMT);
725*33262Ssklower 
726*33262Ssklower       /* now get final page count */
727*33262Ssklower       if (doactng) {
728*33262Ssklower 	pb = pbuf;
729*33262Ssklower 	*pb = '\0';	/* ignore the previous pagecount */
730*33262Ssklower 	while (TRUE) {
731*33262Ssklower 	  r = getc(psin);
732*33262Ssklower 	  if (r == EOF) {
733*33262Ssklower 	      fprintf(stderr, EOFerr, prog, "accounting2");
734*33262Ssklower 	      VOIDC fflush(stderr);
735*33262Ssklower 	      RestoreStatus();
736*33262Ssklower 	      sleep(10);
737*33262Ssklower 	      exit(THROW_AWAY); /* what else to do? */
738*33262Ssklower 	  }
739*33262Ssklower 	  if ((r&0377) == 004) break; /* PS_EOF */
740*33262Ssklower 	  *pb++ = r;
741*33262Ssklower 	}
742*33262Ssklower 	*pb = '\0';
743*33262Ssklower 	debugp((stderr,"%s: accounting 2 (%s)\n",prog,pbuf));
744*33262Ssklower 	if (pb = FindPattern(pb, pbuf, "%%[ pagecount: ")) {
745*33262Ssklower 	    sc = sscanf(pb, "%%%%[ pagecount: %d ]%%%%\r", &pc2);
746*33262Ssklower 	}
747*33262Ssklower 	if ((pb == NULL) || (sc != 1)) {
748*33262Ssklower 	    fprintf(stderr, "%s: accounting error 2 (%s)\n", prog,pbuf);
749*33262Ssklower 	    VOIDC fflush(stderr);
750*33262Ssklower 	}
751*33262Ssklower         else if ((pc2 < pc1) || (pc1 < 0) || (pc2 < 0)) {
752*33262Ssklower 	    fprintf(stderr,"%s: accounting error 3 %d %d\n", prog,pc1,pc2);
753*33262Ssklower 	    VOIDC fflush(stderr);
754*33262Ssklower 	}
755*33262Ssklower 	else if (freopen(accountingfile, "a", stdout) != NULL) {
756*33262Ssklower 	  printf("%7.2f\t%s:%s\n", (float)(pc2 - pc1), host, name);
757*33262Ssklower 	  VOIDC fclose(stdout);
758*33262Ssklower 	}
759*33262Ssklower       }
760*33262Ssklower 
761*33262Ssklower       /* all done -- let sender know */
762*33262Ssklower       if (havejobout) VOIDC fclose(jobout);
763*33262Ssklower       VOIDC fclose(psin);
764*33262Ssklower       exit(0); /* to parent */
765*33262Ssklower     }
766*33262Ssklower }
767*33262Ssklower 
768*33262Ssklower /* send the file ".banner" */
769*33262Ssklower private SendBanner()
770*33262Ssklower {
771*33262Ssklower     register int banner;
772*33262Ssklower     int cnt;
773*33262Ssklower     char buf[BUFSIZ];
774*33262Ssklower 
775*33262Ssklower     if ((banner = open(".banner",O_RDONLY|O_NDELAY,0)) < 0) return;
776*33262Ssklower     while ((cnt = read(banner,buf,sizeof buf)) > 0) {
777*33262Ssklower 	VOIDC write(fdsend,buf,cnt);
778*33262Ssklower     }
779*33262Ssklower     VOIDC close(banner);
780*33262Ssklower }
781*33262Ssklower 
782*33262Ssklower /* search backwards from p in start for patt */
783*33262Ssklower private char *FindPattern(p, start, patt)
784*33262Ssklower char *p;
785*33262Ssklower char *start;
786*33262Ssklower char *patt;
787*33262Ssklower {
788*33262Ssklower     int patlen;
789*33262Ssklower     patlen = strlen(patt);
790*33262Ssklower 
791*33262Ssklower     p -= patlen;
792*33262Ssklower     for (; p >= start; p--) {
793*33262Ssklower 	if (strncmp(p, patt, patlen) == 0) return(p);
794*33262Ssklower     }
795*33262Ssklower     return ((char *)NULL);
796*33262Ssklower }
797*33262Ssklower 
798*33262Ssklower private GotChar(c)
799*33262Ssklower register int c;
800*33262Ssklower {
801*33262Ssklower     static char linebuf[BUFSIZ];
802*33262Ssklower     static char *cp = linebuf;
803*33262Ssklower     static enum State {normal, onep, twop, inmessage,
804*33262Ssklower     			close1, close2, close3, close4} st = normal;
805*33262Ssklower     char *match, *last;
806*33262Ssklower 
807*33262Ssklower     switch (st) {
808*33262Ssklower 	case normal:
809*33262Ssklower 	    if (c == '%') {
810*33262Ssklower 		st = onep;
811*33262Ssklower 		cp = linebuf;
812*33262Ssklower 		*cp++ = c;
813*33262Ssklower 		break;
814*33262Ssklower 	    }
815*33262Ssklower 	    putc(c,jobout);
816*33262Ssklower 	    VOIDC fflush(jobout);
817*33262Ssklower 	    break;
818*33262Ssklower 	case onep:
819*33262Ssklower 	    if (c == '%') {
820*33262Ssklower 		st = twop;
821*33262Ssklower 		*cp++ = c;
822*33262Ssklower 		break;
823*33262Ssklower 	    }
824*33262Ssklower 	    putc('%',jobout);
825*33262Ssklower 	    putc(c,jobout);
826*33262Ssklower 	    VOIDC fflush(jobout);
827*33262Ssklower 	    st = normal;
828*33262Ssklower 	    break;
829*33262Ssklower 	case twop:
830*33262Ssklower 	    if (c == '\[') {
831*33262Ssklower 		st = inmessage;
832*33262Ssklower 		*cp++ = c;
833*33262Ssklower 		break;
834*33262Ssklower 	    }
835*33262Ssklower 	    if (c == '\%') {
836*33262Ssklower 		putc('%',jobout);
837*33262Ssklower 		VOIDC fflush(jobout);
838*33262Ssklower 		/* don't do anything to cp */
839*33262Ssklower 		break;
840*33262Ssklower 	    }
841*33262Ssklower 	    putc('%',jobout);
842*33262Ssklower 	    putc('%',jobout);
843*33262Ssklower 	    VOIDC fflush(jobout);
844*33262Ssklower 	    st = normal;
845*33262Ssklower 	    break;
846*33262Ssklower 	case inmessage:
847*33262Ssklower 	    *cp++ = c;
848*33262Ssklower 	    if (c == '\]') st = close1;
849*33262Ssklower 	    break;
850*33262Ssklower 	case close1:
851*33262Ssklower 	    *cp++ = c;
852*33262Ssklower 	    switch (c) {
853*33262Ssklower 		case '%': st = close2; break;
854*33262Ssklower 		case '\]': st = close1; break;
855*33262Ssklower 		default: st = inmessage; break;
856*33262Ssklower 	    }
857*33262Ssklower 	    break;
858*33262Ssklower 	case close2:
859*33262Ssklower 	    *cp++ = c;
860*33262Ssklower 	    switch (c) {
861*33262Ssklower 		case '%': st = close3; break;
862*33262Ssklower 		case '\]': st = close1; break;
863*33262Ssklower 		default: st = inmessage; break;
864*33262Ssklower 	    }
865*33262Ssklower 	    break;
866*33262Ssklower 	case close3:
867*33262Ssklower 	    *cp++ = c;
868*33262Ssklower 	    switch (c) {
869*33262Ssklower 		case '\r': st = close4; break;
870*33262Ssklower 		case '\]': st = close1; break;
871*33262Ssklower 		default: st = inmessage; break;
872*33262Ssklower 	    }
873*33262Ssklower 	    break;
874*33262Ssklower 	case close4:
875*33262Ssklower 	    *cp++ = c;
876*33262Ssklower 	    switch(c) {
877*33262Ssklower 		case '\n': st = normal; break;
878*33262Ssklower 		case '\]': st = close1; break;
879*33262Ssklower 		default: st = inmessage; break;
880*33262Ssklower 	    }
881*33262Ssklower 	    if (st == normal) {
882*33262Ssklower 		/* parse complete message */
883*33262Ssklower 		last = cp;
884*33262Ssklower 		*cp = 0;
885*33262Ssklower 		debugp((stderr,">>%s",linebuf));
886*33262Ssklower 		if (match = FindPattern(cp, linebuf, " PrinterError: ")) {
887*33262Ssklower 		    if (*(match-1) != ':') {
888*33262Ssklower 			fprintf(stderr,"%s",linebuf);
889*33262Ssklower 			VOIDC fflush(stderr);
890*33262Ssklower 			*(last-6) = 0;
891*33262Ssklower 			Status(match+15);
892*33262Ssklower 		    }
893*33262Ssklower 		    else {
894*33262Ssklower 			last = index(match,';');
895*33262Ssklower 			*last = 0;
896*33262Ssklower 			Status(match+15);
897*33262Ssklower 		    }
898*33262Ssklower 		}
899*33262Ssklower 		else if (match = FindPattern(cp, linebuf, " status: ")) {
900*33262Ssklower 		    match += 9;
901*33262Ssklower 		    if (strncmp(match,"idle",4) == 0) {
902*33262Ssklower 			/* we are hopelessly lost, get everyone to quit */
903*33262Ssklower 			fprintf(stderr,"%s: ERROR: printer is idle, giving up!\n",prog);
904*33262Ssklower 			VOIDC fflush(stderr);
905*33262Ssklower 			VOIDC kill(getppid(),SIGKILL); /* will this work */
906*33262Ssklower 			exit(THROW_AWAY);
907*33262Ssklower 		    }
908*33262Ssklower 		    else {
909*33262Ssklower 			/* one of: busy, waiting, printing, initializing */
910*33262Ssklower 			/* clear status message */
911*33262Ssklower 			RestoreStatus();
912*33262Ssklower 		    }
913*33262Ssklower 		}
914*33262Ssklower 		else {
915*33262Ssklower 		    /* message not for us */
916*33262Ssklower 		    fprintf(jobout,"%s",linebuf);
917*33262Ssklower 		    VOIDC fflush(jobout);
918*33262Ssklower 		    st = normal;
919*33262Ssklower 		    break;
920*33262Ssklower 		}
921*33262Ssklower 	    }
922*33262Ssklower 	    break;
923*33262Ssklower 	default:
924*33262Ssklower 	    fprintf(stderr,"bad case;\n");
925*33262Ssklower     }
926*33262Ssklower     return;
927*33262Ssklower }
928*33262Ssklower 
929*33262Ssklower /* backup "status" message file in ".status",
930*33262Ssklower  * in case there is a PrinterError
931*33262Ssklower  */
932*33262Ssklower 
933*33262Ssklower private BackupStatus(file1, file2)
934*33262Ssklower char *file1, *file2;
935*33262Ssklower {
936*33262Ssklower     register int fd1, fd2;
937*33262Ssklower     char buf[BUFSIZ];
938*33262Ssklower     int cnt;
939*33262Ssklower 
940*33262Ssklower     VOIDC umask(0);
941*33262Ssklower     fd1 = open(file1, O_WRONLY|O_CREAT, 0664);
942*33262Ssklower     if ((fd1 < 0) || (flock(fd1,LOCK_EX) < 0)) {
943*33262Ssklower 	VOIDC unlink(file1);
944*33262Ssklower 	VOIDC flock(fd1,LOCK_UN);
945*33262Ssklower 	VOIDC close(fd1);
946*33262Ssklower 	fd1 = open(file1, O_WRONLY|O_CREAT, 0664);
947*33262Ssklower     }
948*33262Ssklower     if ((fd1 < 0) || (flock(fd1,LOCK_EX) <0)) {
949*33262Ssklower 	fprintf(stderr, "%s: writing %s:\n",prog,file1);
950*33262Ssklower 	perror(prog);
951*33262Ssklower 	VOIDC close(fd1);
952*33262Ssklower 	return;
953*33262Ssklower     }
954*33262Ssklower     VOIDC ftruncate(fd1,0);
955*33262Ssklower     if ((fd2 = open(file2, O_RDONLY,0)) < 0) {
956*33262Ssklower 	fprintf(stderr, "%s: error reading %s:\n", prog, file2);
957*33262Ssklower 	perror(prog);
958*33262Ssklower 	VOIDC close(fd1);
959*33262Ssklower 	return;
960*33262Ssklower     }
961*33262Ssklower     cnt = read(fd2,buf,BUFSIZ);
962*33262Ssklower     VOIDC write(fd1,buf,cnt);
963*33262Ssklower     VOIDC flock(fd1,LOCK_UN);
964*33262Ssklower     VOIDC close(fd1);
965*33262Ssklower     VOIDC close(fd2);
966*33262Ssklower }
967*33262Ssklower 
968*33262Ssklower /* restore the "status" message from the backed-up ".status" copy */
969*33262Ssklower private RestoreStatus() {
970*33262Ssklower     BackupStatus("status",".status");
971*33262Ssklower }
972*33262Ssklower 
973*33262Ssklower /* report PrinterError via "status" message file */
974*33262Ssklower private Status(msg)
975*33262Ssklower register char *msg;
976*33262Ssklower {
977*33262Ssklower     register int fd;
978*33262Ssklower     char msgbuf[100];
979*33262Ssklower 
980*33262Ssklower     if ((fd = open("status",O_WRONLY|O_CREAT,0664)) < 0) return;
981*33262Ssklower     VOIDC ftruncate(fd,0);
982*33262Ssklower     sprintf(msgbuf,"Printer Error: may need attention! (%s)\n\0",msg);
983*33262Ssklower     VOIDC write(fd,msgbuf,strlen(msgbuf));
984*33262Ssklower     VOIDC close(fd);
985*33262Ssklower }
986*33262Ssklower 
987*33262Ssklower /* sending phase alarm handler for sender */
988*33262Ssklower 
989*33262Ssklower private VOID salarm() {
990*33262Ssklower 
991*33262Ssklower     debugp((stderr,"%s: AS %d %d %d\n",prog,oldprogress,progress,getstatus));
992*33262Ssklower 
993*33262Ssklower     /* if progress != oldprogress, we made some progress (sent something)
994*33262Ssklower      * else, we had two alarms without sending anything...
995*33262Ssklower      * It may be that a PrinterError has us stopped, or we are computing
996*33262Ssklower      * for a long time (forever?) -- printer jobtimeout may help here
997*33262Ssklower      * in any case, all we do is set the flag to get status...
998*33262Ssklower      * this will help us clear printererror notification
999*33262Ssklower      */
1000*33262Ssklower 
1001*33262Ssklower     oldprogress = progress;
1002*33262Ssklower     getstatus = TRUE;
1003*33262Ssklower 
1004*33262Ssklower     /* reset the alarm and return */
1005*33262Ssklower     VOIDC alarm(SENDALARM);
1006*33262Ssklower     return;
1007*33262Ssklower }
1008*33262Ssklower 
1009*33262Ssklower /* waiting phase alarm handler for sender */
1010*33262Ssklower 
1011*33262Ssklower private VOID walarm() {
1012*33262Ssklower     static int acount = 0;
1013*33262Ssklower 
1014*33262Ssklower     debugp((stderr,"%s: WA %d %d %d %d\n",
1015*33262Ssklower     	prog,acount,oldprogress,progress,getstatus));
1016*33262Ssklower 
1017*33262Ssklower     if ((oldprogress != progress) || (acount == 4)) {
1018*33262Ssklower 	getstatus = TRUE;
1019*33262Ssklower 	acount = 0;
1020*33262Ssklower 	oldprogress = progress;
1021*33262Ssklower     }
1022*33262Ssklower     else acount++;
1023*33262Ssklower 
1024*33262Ssklower     /* reset alarm */
1025*33262Ssklower     VOIDC alarm(WAITALARM);
1026*33262Ssklower 
1027*33262Ssklower     /* return to wait loop */
1028*33262Ssklower     longjmp(dwait, 0);
1029*33262Ssklower }
1030*33262Ssklower 
1031*33262Ssklower /* final phase alarm handler for sender */
1032*33262Ssklower 
1033*33262Ssklower private VOID falarm() {
1034*33262Ssklower 
1035*33262Ssklower     debugp((stderr,"%s: FA %d %d %d\n",prog,oldprogress,progress,getstatus));
1036*33262Ssklower 
1037*33262Ssklower     /* no reason to count progress, just get status */
1038*33262Ssklower     if (!intrup) {
1039*33262Ssklower 	VOIDC write(fdsend, statusbuf, 1);
1040*33262Ssklower     }
1041*33262Ssklower     getstatus = FALSE;
1042*33262Ssklower 
1043*33262Ssklower     /* reset alarm */
1044*33262Ssklower     VOIDC alarm(WAITALARM);
1045*33262Ssklower     return;
1046*33262Ssklower }
1047*33262Ssklower 
1048*33262Ssklower /* initial interrupt handler - before communications begin, so
1049*33262Ssklower  * nothing to be sent to printer
1050*33262Ssklower  */
1051*33262Ssklower private VOID intinit() {
1052*33262Ssklower     long clock;
1053*33262Ssklower 
1054*33262Ssklower     /* get rid of banner file */
1055*33262Ssklower     VOIDC unlink(".banner");
1056*33262Ssklower 
1057*33262Ssklower     fprintf(stderr,"%s: abort (during setup)\n",prog);
1058*33262Ssklower     VOIDC fflush(stderr);
1059*33262Ssklower 
1060*33262Ssklower     /* these next two may be too cautious */
1061*33262Ssklower     VOIDC kill(0,SIGINT);
1062*33262Ssklower     while (wait((union wait *) 0) > 0);
1063*33262Ssklower 
1064*33262Ssklower     if (VerboseLog) {
1065*33262Ssklower 	fprintf (stderr, "%s: end - %s", prog, (time(&clock), ctime(&clock)));
1066*33262Ssklower 	VOIDC fflush(stderr);
1067*33262Ssklower     }
1068*33262Ssklower 
1069*33262Ssklower     exit(THROW_AWAY);
1070*33262Ssklower }
1071*33262Ssklower 
1072*33262Ssklower /* interrupt during sending phase to sender process */
1073*33262Ssklower 
1074*33262Ssklower private VOID intsend() {
1075*33262Ssklower     /* set flag */
1076*33262Ssklower     intrup = TRUE;
1077*33262Ssklower     longjmp(sendint, 0);
1078*33262Ssklower }
1079*33262Ssklower 
1080*33262Ssklower /* interrupt during waiting phase to sender process */
1081*33262Ssklower 
1082*33262Ssklower private VOID intwait() {
1083*33262Ssklower 
1084*33262Ssklower     intrup = TRUE;
1085*33262Ssklower 
1086*33262Ssklower     fprintf(stderr,"%s: abort (waiting)\n",prog);
1087*33262Ssklower     VOIDC fflush(stderr);
1088*33262Ssklower     if (ioctl(fdsend, TIOCFLUSH, &flg) || ioctl(fdsend, TIOCSTART, &flg)
1089*33262Ssklower     || (write(fdsend, abortbuf, 1) != 1)) {
1090*33262Ssklower 	fprintf(stderr, "%s: error in ioctl(fdsend):\n", prog);
1091*33262Ssklower 	perror(prog);
1092*33262Ssklower     }
1093*33262Ssklower 
1094*33262Ssklower     /* VOIDC alarm(2); /* force an alarm soon to get us out of wait! ? */
1095*33262Ssklower     longjmp(dwait, 0);
1096*33262Ssklower }
1097*33262Ssklower 
1098*33262Ssklower /* EMT for reverse filter, avoid printer timeout at the expense
1099*33262Ssklower  * of performance (sigh)
1100*33262Ssklower  */
1101*33262Ssklower 
1102*33262Ssklower private VOID reverseready() {
1103*33262Ssklower     revdone = TRUE;
1104*33262Ssklower     longjmp(waitonreverse, 0);
1105*33262Ssklower }
1106*33262Ssklower 
1107*33262Ssklower /* EMT on startup to sender -- signalled by listener after first status
1108*33262Ssklower  * message received
1109*33262Ssklower  */
1110*33262Ssklower 
1111*33262Ssklower private VOID readynow() {
1112*33262Ssklower     goahead = TRUE;
1113*33262Ssklower     longjmp(startstatus, 0);
1114*33262Ssklower }
1115*33262Ssklower 
1116*33262Ssklower /* EMT on sending phase, hard EOF printer died! */
1117*33262Ssklower private VOID emtdead() {
1118*33262Ssklower     VOIDC alarm(0);
1119*33262Ssklower     exit(THROW_AWAY);
1120*33262Ssklower }
1121*33262Ssklower 
1122*33262Ssklower /* EMT during waiting phase -- listener saw an EOF (^D) from printer */
1123*33262Ssklower 
1124*33262Ssklower private VOID emtdone() {
1125*33262Ssklower     VOIDC alarm(0);
1126*33262Ssklower     gotemt = TRUE;
1127*33262Ssklower     longjmp(dwait, 0);
1128*33262Ssklower }
1129