xref: /csrg-svn/local/transcript/src/pscomm.c (revision 33796)
133262Ssklower #ifndef lint
233262Ssklower static char Notice[] = "Copyright (c) 1985 Adobe Systems Incorporated";
333263Ssklower static char *ORCSID="$Header: pscomm.bsd,v 2.1 85/11/24 11:50:16 shore Rel $";
433263Ssklower static char *RCSID="$Header: pscomm.c,v 1.4 87/10/31 20:42:02 cuong Exp $";
533262Ssklower #endif
633262Ssklower /* pscomm.c
733262Ssklower  *
833262Ssklower  * Copyright (C) 1985 Adobe Systems Incorporated
933262Ssklower  *
1033262Ssklower  * 4.2BSD lpr/lpd communications filter for PostScript printers
1133262Ssklower  * (formerly "psif" in TranScript release 1.0)
1233262Ssklower  *
1333262Ssklower  * pscomm is the general communications filter for
1433262Ssklower  * sending files to a PostScript printer (e.g., an Apple LaserWriter,
1533262Ssklower  * QMS PostScript printer, or Linotype PostScript typesetter)
1633262Ssklower  * via RS232 lines.  It does page accounting, error handling/reporting,
1733262Ssklower  * job logging, banner page printing, etc.
1833262Ssklower  * It observes (parts of) the PostScript file structuring conventions.
1933262Ssklower  * In particular, it distinguishes between PostScript files (beginning
2033262Ssklower  * with the "%!" magic number) -- which are shipped to the printer --
2133262Ssklower  * and text files (no magic number) which are formatted and listed
2233262Ssklower  * on the printer.  Files which begin with "%!PS-Adobe-" may be
2333262Ssklower  * page-reversed if the target printer has that option specified.
2433262Ssklower  *
2533262Ssklower  * depending on the values of BANNERFIRST and BANNERLAST,
2633262Ssklower  * pscomm looks for a file named ".banner", (created by the "of" filter)
2733262Ssklower  * in the current working directory and ships it to the printer also.
2833262Ssklower  *
2933262Ssklower  * pscomm gets called with:
3033262Ssklower  *	stdin	== the file to print (may be a pipe!)
3133262Ssklower  *	stdout	== the printer
3233262Ssklower  *	stderr	== the printer log file
3333262Ssklower  *	cwd	== the spool directory
3433262Ssklower  *	argv	== set up by interface shell script:
3533262Ssklower  *	  filtername	-P printer
3633262Ssklower  *			-p filtername
3733262Ssklower  *			[-r]		(don't ever reverse)
3833262Ssklower  *			-n login
3933262Ssklower  *			-h host
4033262Ssklower  *			[accntfile]
4133262Ssklower  *
4233262Ssklower  *	environ	== various environment variable effect behavior
4333262Ssklower  *		VERBOSELOG	- do verbose log file output
4433262Ssklower  *		BANNERFIRST	- print .banner before job
4533262Ssklower  *		BANNERLAST	- print .banner after job
4633262Ssklower  *		REVERSE		- page reversal filter program
4733262Ssklower  *				  (no reversal if null or missing)
4833262Ssklower  *		PSLIBDIR	- transcript library directory
4933262Ssklower  *		PSTEXT		- simple text formatting filter
5033262Ssklower  *		JOBOUTPUT	- file for actual printer stream
5133262Ssklower  *				  output (if defined)
5233262Ssklower  *
5333262Ssklower  * pscomm depends on certain additional features of the 4.2BSD spooling
5433262Ssklower  * architecture.  In particular it assumes that the printer status file
5533262Ssklower  * has the default name (./status) and it uses this file to communicate
5633262Ssklower  * printer error status information to the user -- the contents of the
5733262Ssklower  * status file gets incorporated in "lpq" and "lpc status" messages.
5833262Ssklower  *
5933262Ssklower  * Edit History:
6033262Ssklower  * Andrew Shore: Sat Nov 16 11:59:58 1985
6133262Ssklower  * End Edit History.
6233262Ssklower  *
6333262Ssklower  * RCSLOG:
6433263Ssklower  * $Log:	pscomm.c,v $
6533263Ssklower  * Revision 1.4  87/10/31  20:42:02  cuong
6633263Ssklower  * Two changes:
6733263Ssklower  *     1. Make sender wait for listener's signal when requesting initial
6833263Ssklower  *        pagecount.  The problem is with short jobs & fast machines;
6933263Ssklower  *        the sender will merrily finish the job and send an asynchronous
7033263Ssklower  *        status request the response to which will clobber the listener's
7133263Ssklower  *        input stream.
7233263Ssklower  *     2. Make sender sleep(1) just after sending the job-finish EOF to give
7333263Ssklower  *        the LaserWriter a chance to update its status.
7433263Ssklower  *
7533263Ssklower  * Revision 1.3  87/10/03  16:42:47  cuong
7633263Ssklower  * Takes care of improper handling of abnormal exits when
7733263Ssklower  * accounting is turned on.  Pagecount information was again
7833263Ssklower  * waited on abortively.  Now, it's fixed, no?
7933263Ssklower  *
8033263Ssklower  * Revision 1.2  87/09/20  19:03:25  cuong
8133263Ssklower  * Fixed bug:
8233263Ssklower  * Symptom: pagecount accounting turned on, then pscomms will hang.
8333263Ssklower  * Reason: old pscomm listener assumed the final pagecount information
8433263Ssklower  *         will come after the ctrl-d; this is not true.  Hence it
8533263Ssklower  * 	hangs waiting after the ctrl-d is received.
8633263Ssklower  * Fix:    while waiting for ctrl-d, the pscomm listener must also
8733263Ssklower  *         scan for the pattern %%[ pagecount: %d ]%%, and save
8833263Ssklower  *         this in the pbuf[] array if found.
8933263Ssklower  * Cuong
9033263Ssklower  *
9133263Ssklower  * Revision 1.1  87/06/13  19:26:31  cuong
9233263Ssklower  * Initial revision
9333263Ssklower  *
9433262Ssklower  * Revision 2.1  85/11/24  11:50:16  shore
9533262Ssklower  * Product Release 2.0
9633262Ssklower  *
9733262Ssklower  * Revision 1.1  85/11/20  00:35:21  shore
9833262Ssklower  * Initial revision
9933262Ssklower  *
10033262Ssklower  * Revision 1.2  85/05/14  11:25:29  shore
10133262Ssklower  * better support for BANNERLAST, still buggy though
10233262Ssklower  *
10333262Ssklower  *
10433262Ssklower  */
10533262Ssklower 
10633262Ssklower #include <ctype.h>
10733262Ssklower #include <setjmp.h>
10833262Ssklower #include <sgtty.h>
10933262Ssklower #include <signal.h>
11033262Ssklower #include <stdio.h>
11133262Ssklower #include <strings.h>
11233262Ssklower 
11333262Ssklower #include <sys/file.h>
11433262Ssklower #include <sys/ioctl.h>
11533262Ssklower #include <sys/time.h>
11633262Ssklower #include <sys/resource.h>
11733262Ssklower #include <sys/wait.h>
11833262Ssklower #include <sys/types.h>
11933262Ssklower #include <sys/stat.h>
12033262Ssklower 
12133262Ssklower #include "transcript.h"
12233262Ssklower #include "psspool.h"
12333262Ssklower 
12433262Ssklower #ifdef BDEBUG
12533263Ssklower #define debugp(x) \
12633263Ssklower { \
12733263Ssklower    fprintf(stderr, "(pid %d) ", getpid()); \
12833263Ssklower    fprintf x; \
12933263Ssklower    (void) fflush(stderr); \
13033263Ssklower }
13133262Ssklower #else
13233262Ssklower #define debugp(x)
13333262Ssklower #endif BDEBUG
13433262Ssklower 
13533262Ssklower /*
13633262Ssklower  * the following string is sent to the printer when we want it to
13733262Ssklower  * report its current pagecount (for accounting)
13833262Ssklower  */
13933262Ssklower 
14033262Ssklower private char *getpages = "\n(%%%%[ pagecount: )print \
14133262Ssklower statusdict/pagecount get exec(                )cvs print( ]%%%%)= flush\n%s";
14233262Ssklower 
14333262Ssklower private jmp_buf waitonreverse, startstatus, dwait, sendint;
14433262Ssklower 
14533262Ssklower private char	*prog;			/* invoking program name */
14633262Ssklower private char	*name;			/* user login name */
14733262Ssklower private char	*host;			/* host name */
14833262Ssklower private char	*pname;			/* printer name */
14933262Ssklower private char	*accountingfile;	/* file for printer accounting */
15033262Ssklower private int	doactng;		/* true if we can do accounting */
15133262Ssklower private int	progress, oldprogress;	/* finite progress counts */
15233262Ssklower private int	getstatus = FALSE;
15333262Ssklower private int	revdone = FALSE;	/* reverse done, send new */
15433262Ssklower private int	goahead = FALSE;	/* got initial status back */
15533262Ssklower private int	gotemt = FALSE;		/* got ^D ack from listener */
15633262Ssklower private int	sendend = TRUE;		/* send an ^D */
15733262Ssklower 
15833262Ssklower private char *bannerfirst;
15933262Ssklower private char *bannerlast;
16033262Ssklower private char *verboselog;
16133262Ssklower private char *reverse;
16233262Ssklower private int BannerFirst;
16333262Ssklower private int BannerLast;
16433262Ssklower private int VerboseLog;
16533262Ssklower 
16633262Ssklower private int	fpid = 0;	/* formatter pid */
16733262Ssklower private int	cpid = 0;	/* listener pid */
16833262Ssklower 
16933262Ssklower private int	intrup = FALSE;	/* interrupt flag */
17033262Ssklower 
17133262Ssklower private char abortbuf[] = "\003";	/* ^C abort */
17233262Ssklower private char statusbuf[] = "\024";	/* ^T status */
17333262Ssklower private char eofbuf[] = "\004";		/* ^D end of file */
17433262Ssklower 
17533262Ssklower private char EOFerr[] = "%s: unexpected EOF from printer (%s)!\n";
17633262Ssklower 
17733262Ssklower /* global file descriptors (avoid stdio buffering!) */
17833262Ssklower private int fdsend;		/* to printer (from stdout) */
17933262Ssklower private int fdlisten;		/* from printer (same tty line) */
18033262Ssklower private int fdinput;		/* file to print (from stdin) */
18133262Ssklower 
18233262Ssklower private FILE *jobout;		/* special printer output log */
18333262Ssklower 
18433262Ssklower private int flg = FREAD|FWRITE;	 /* ioctl FLUSH arg */
18533262Ssklower 
18633262Ssklower 
18733262Ssklower extern char *getenv();
18833262Ssklower 
18933262Ssklower private VOID	intinit();
19033262Ssklower private VOID	intsend();
19133262Ssklower private VOID	intwait();
19233262Ssklower private VOID	salarm();
19333262Ssklower private VOID	walarm();
19433262Ssklower private VOID	falarm();
19533262Ssklower private VOID	reverseready();
19633262Ssklower private VOID	readynow();
19733262Ssklower private VOID	emtdead();
19833262Ssklower private VOID	emtdone();
19933262Ssklower private char 	*FindPattern();
20033262Ssklower 
20133262Ssklower #define SENDALARM 90
20233262Ssklower #define WAITALARM 30
20333262Ssklower 
20433262Ssklower main(argc,argv)
20533262Ssklower 	int argc;
20633262Ssklower 	char *argv[];
20733262Ssklower {
20833262Ssklower     register char  *cp;
20933262Ssklower     register int cnt, wc;
21033262Ssklower     register char *mbp;
21133262Ssklower 
21233262Ssklower     char  **av;
21333262Ssklower     long clock;		/* for log timestamp */
21433262Ssklower     char magic[11];	/* first few bytes of stdin ?magic number and type */
21533262Ssklower     int  noReverse = 0; /* flag if we should never page reverse */
21633262Ssklower     int  canReverse = 0;/* flag if we can page-reverse the ps file */
21733262Ssklower     int  reversing = 0;
21833262Ssklower     FILE *streamin;
21933262Ssklower 
22033262Ssklower     char mybuf[BUFSIZ];
22133262Ssklower     int wpid;
22233262Ssklower     union wait status;
22333262Ssklower     int fdpipe[2];
22433262Ssklower     int format = 0;
22533262Ssklower     int i;
22633262Ssklower 
22733262Ssklower     VOIDC signal(SIGINT, intinit);
22833262Ssklower     VOIDC signal(SIGHUP, intinit);
22933262Ssklower     VOIDC signal(SIGQUIT, intinit);
23033262Ssklower     VOIDC signal(SIGTERM, intinit);
23133262Ssklower 
23233262Ssklower     /* parse command-line arguments */
23333262Ssklower     /* the argv (see header comments) comes from the spooler daemon */
23433262Ssklower     /* itself, so it should be canonical, but at least one 4.2-based */
23533262Ssklower     /* system uses -nlogin -hhost (insead of -n login -h host) so I */
23633262Ssklower     /* check for both */
23733262Ssklower 
23833262Ssklower     av = argv;
23933262Ssklower     prog = *av;
24033262Ssklower 
24133262Ssklower     while (--argc) {
24233262Ssklower 	if (*(cp = *++av) == '-') {
24333262Ssklower 	    switch (*(cp + 1)) {
24433262Ssklower 		case 'P':	/* printer name */
24533262Ssklower 		    argc--;
24633262Ssklower 		    pname = *(++av);
24733262Ssklower 		    break;
24833262Ssklower 
24933262Ssklower 		case 'n': 	/* user name */
25033262Ssklower 		    argc--;
25133262Ssklower 		    name = *(++av);
25233262Ssklower 		    break;
25333262Ssklower 
25433262Ssklower 		case 'h': 	/* host */
25533262Ssklower 		    argc--;
25633262Ssklower 		    host = *(++av);
25733262Ssklower 		    break;
25833262Ssklower 
25933262Ssklower 		case 'p':	/* prog */
26033262Ssklower 		    argc--;
26133262Ssklower 		    prog = *(++av);
26233262Ssklower 		    break;
26333262Ssklower 
26433262Ssklower 		case 'r':	/* never reverse */
26533262Ssklower 		    argc--;
26633262Ssklower 		    noReverse = 1;
26733262Ssklower 		    break;
26833262Ssklower 
26933262Ssklower 		default:	/* unknown */
27033262Ssklower 		    fprintf(stderr,"%s: unknown option: %s\n",prog,cp);
27133262Ssklower 		    break;
27233262Ssklower 	    }
27333262Ssklower 	}
27433262Ssklower 	else
27533262Ssklower 	    accountingfile = cp;
27633262Ssklower     }
27733262Ssklower 
27833262Ssklower     debugp((stderr,"args: %s %s %s %s\n",prog,host,name,accountingfile));
27933262Ssklower 
28033262Ssklower     /* do printer-specific options processing */
28133262Ssklower 
28233262Ssklower     VerboseLog = 1;
28333262Ssklower     BannerFirst = BannerLast = 0;
28433262Ssklower     reverse = NULL;
28533262Ssklower     if (bannerfirst=envget("BANNERFIRST")) {
28633262Ssklower 	BannerFirst=atoi(bannerfirst);
28733262Ssklower     }
28833262Ssklower     if (bannerlast=envget("BANNERLAST")) {
28933262Ssklower 	BannerLast=atoi(bannerlast);
29033262Ssklower     }
29133262Ssklower     if (verboselog=envget("VERBOSELOG")) {
29233262Ssklower 	VerboseLog=atoi(verboselog);
29333262Ssklower     }
29433262Ssklower     if (!noReverse) {
29533262Ssklower 	reverse=envget("REVERSE");	/* name of the filter itself */
29633262Ssklower     }
29733262Ssklower 
29833262Ssklower     if (VerboseLog) {
29933262Ssklower 	fprintf(stderr, "%s: %s:%s %s start - %s", prog, host, name, pname,
30033262Ssklower             (VOIDC time(&clock), ctime(&clock)));
30133262Ssklower 	VOIDC fflush(stderr);
30233262Ssklower     }
30333262Ssklower     debugp((stderr,"%s: pid %d ppid %d\n",prog,getpid(),getppid()));
30433262Ssklower     debugp((stderr,"%s: options BF %d BL %d VL %d R %s\n",prog,BannerFirst,
30533262Ssklower     	BannerLast, VerboseLog, ((reverse == NULL) ? "norev": reverse)));
30633262Ssklower 
30733262Ssklower     /* IMPORTANT: in the case of cascaded filters, */
30833262Ssklower     /* stdin may be a pipe! (and hence we cannot seek!) */
30933262Ssklower 
31033262Ssklower     if ((cnt = read(fileno(stdin),magic,11)) != 11) goto badfile;
31133262Ssklower     debugp((stderr,"%s: magic number is %11.11s\n",prog,magic));
31233262Ssklower     streamin = stdin;
31333262Ssklower 
31433262Ssklower     if (strncmp(magic,"%!PS-Adobe-",11) == 0) {
31533262Ssklower 	canReverse = TRUE;
31633262Ssklower 	goto go_ahead;
31733262Ssklower     }
31833262Ssklower     else if (strncmp(magic,"%!",2) == 0) {
31933262Ssklower 	canReverse = FALSE;
32033262Ssklower 	goto go_ahead;
32133262Ssklower     }
32233262Ssklower 
32333262Ssklower     /* here is where you might test for other file type
32433262Ssklower      * e.g., PRESS, imPRESS, DVI, Mac-generated, etc.
32533262Ssklower      */
32633262Ssklower 
32733262Ssklower     /* final sanity check on the text file, to guard
32833262Ssklower      * against arbitrary binary data
32933262Ssklower      */
33033262Ssklower 
33133262Ssklower     for (i = 0; i < 11; i++) {
33233262Ssklower 	if (!isascii(magic[i]) || (!isprint(magic[i]) && !isspace(magic[i]))){
33333262Ssklower 	    fprintf(stderr,"%s: spooled binary file rejected\n",prog);
33433262Ssklower 	    VOIDC fflush(stderr);
33533262Ssklower 	    sprintf(mybuf,"%s/bogusmsg.ps",envget("PSLIBDIR"));
33633262Ssklower 	    if ((streamin = freopen(mybuf,"r",stdin)) == NULL) {
33733262Ssklower 		exit(THROW_AWAY);
33833262Ssklower 	    }
33933262Ssklower 	    format = 1;
34033262Ssklower 	    goto lastchance;
34133262Ssklower 	}
34233262Ssklower     }
34333262Ssklower 
34433262Ssklower     goto format_text;
34533262Ssklower 
34633262Ssklower     badfile:
34733262Ssklower         fprintf(stderr,"%s: bad magic number, EOF\n", prog);
34833262Ssklower 	VOIDC fflush(stderr);
34933262Ssklower 	exit(THROW_AWAY);
35033262Ssklower 
35133262Ssklower     format_text:
35233262Ssklower         /* exec dumb formatter to make a listing */
35333262Ssklower 	    debugp((stderr,"formatting\n"));
35433262Ssklower 	    format = 1;
35533262Ssklower 	    VOIDC lseek(0,0L,0);
35633262Ssklower 	    rewind(stdin);
35733262Ssklower 	    if (pipe (fdpipe)) pexit2(prog, "format pipe",THROW_AWAY);
35833262Ssklower 	    if ((fpid = fork()) < 0) pexit2(prog, "format fork",THROW_AWAY);
35933262Ssklower 	    if (fpid == 0) { /* child */
36033262Ssklower 		/* set up child stdout to feed parent stdin */
36133262Ssklower 		if (close(1) || (dup(fdpipe[1]) != 1)
36233262Ssklower 		|| close(fdpipe[1]) || close(fdpipe[0])) {
36333262Ssklower 		    pexit2(prog, "format child",THROW_AWAY);
36433262Ssklower 		}
36533262Ssklower 		execl(envget("PSTEXT"), "pstext", pname, 0);
36633262Ssklower 	   	pexit2(prog,"format exec",THROW_AWAY);
36733262Ssklower 	    }
36833262Ssklower 	/* parent continues */
36933262Ssklower 	/* set up stdin to be pipe */
37033262Ssklower 	if (close(0) || (dup(fdpipe[0]) != 0)
37133262Ssklower 	|| close(fdpipe[0]) || close(fdpipe[1])) {
37233262Ssklower 	    pexit2(prog, "format parent",THROW_AWAY);
37333262Ssklower 	}
37433262Ssklower 
37533262Ssklower 	/* fall through to spooler with new stdin */
37633262Ssklower 	/* can't seek here but we should be at the right place */
37733262Ssklower 	streamin = fdopen(0,"r");
37833262Ssklower 	canReverse = TRUE; /* we know we can reverse pstext output */
37933262Ssklower 
38033262Ssklower     go_ahead:
38133262Ssklower 
38233262Ssklower     /* do page reversal if specified */
38333262Ssklower     if (reversing = ((reverse != NULL) && canReverse)) {
38433262Ssklower 	debugp((stderr,"reversing\n"));
38533262Ssklower 	VOIDC setjmp(waitonreverse);
38633262Ssklower 	if (!revdone) {
38733262Ssklower 	    VOIDC signal(SIGEMT, reverseready);
38833262Ssklower 	    if (pipe (fdpipe)) pexit2(prog, "reverse pipe", THROW_AWAY);
38933262Ssklower 	    if ((fpid = fork()) < 0) pexit2(prog, "reverse fork", THROW_AWAY);
39033262Ssklower 	    if (fpid == 0) { /* child */
39133262Ssklower 		/* set up child stdout to feed parent stdin */
39233262Ssklower 		if (close(1) || (dup(fdpipe[1]) != 1)
39333262Ssklower 		|| close(fdpipe[1]) || close(fdpipe[0])) {
39433262Ssklower 		    pexit2(prog, "reverse child", THROW_AWAY);
39533262Ssklower 		}
39633262Ssklower 		execl(reverse, "psrv", pname, 0);
39733262Ssklower 		pexit2(prog,"reverse exec",THROW_AWAY);
39833262Ssklower 	    }
39933262Ssklower 	    /* parent continues */
40033262Ssklower 	    if (close(0) || (dup(fdpipe[0]) != 0)
40133262Ssklower 	    || close(fdpipe[0]) || close(fdpipe[1])) {
40233262Ssklower 		pexit2(prog, "reverse parent", THROW_AWAY);
40333262Ssklower 	    }
40433262Ssklower 	    /* fall through to spooler with new stdin */
40533262Ssklower 	    /* VOIDC lseek(0,0L,0); */
40633262Ssklower 	    streamin = fdopen(0,"r");
40733262Ssklower 
40833262Ssklower 	    while (TRUE) {
40933262Ssklower 		if (revdone) break;
41033262Ssklower 		pause();
41133262Ssklower 	    }
41233262Ssklower 	}
41333262Ssklower 	VOIDC signal(SIGEMT, SIG_IGN);
41433262Ssklower 	debugp((stderr,"%s: reverse feeding\n",prog));
41533262Ssklower     }
41633262Ssklower 
41733262Ssklower     lastchance:;
41833262Ssklower 
41933262Ssklower     /* establish an input stream from the printer --
42033262Ssklower      * the printcap entry specifies "rw" and we get
42133262Ssklower      * invoked with stdout == the device, so we
42233262Ssklower      * dup stdout, and reopen it for reading;
42333262Ssklower      * this seems to work fine...
42433262Ssklower      */
42533262Ssklower 
42633262Ssklower     fdinput = fileno(streamin); /* the file to print */
42733262Ssklower     fdsend = fileno(stdout);	/* the printer (write) */
42833262Ssklower 
42933262Ssklower     if ((fdlisten = dup(fdsend)) < 0) /* the printer (read) */
43033262Ssklower        pexit(prog, THROW_AWAY);
43133262Ssklower 
43233262Ssklower     doactng = name && accountingfile && (access(accountingfile, W_OK) == 0);
43333262Ssklower 
43433262Ssklower     /* get control of the "status" message file.
43533262Ssklower      * we copy the current one to ".status" so we can restore it
43633262Ssklower      * on exit (to be clean).
43733262Ssklower      * Our ability to use this is publicized nowhere in the
43833262Ssklower      * 4.2 lpr documentation, so things might go bad for us.
43933262Ssklower      * We will use it to report that printer errors condition
44033262Ssklower      * has been detected, and the printer should be checked.
44133262Ssklower      * Unfortunately, this notice may persist through
44233262Ssklower      * the end of the print job, but this is no big deal.
44333262Ssklower      */
44433262Ssklower     BackupStatus(".status","status");
44533262Ssklower 
44633262Ssklower     if ((cpid = fork()) < 0) pexit(prog, THROW_AWAY);
44733262Ssklower     else if (cpid) {/* parent - sender */
44833262Ssklower 	VOIDC setjmp(sendint);
44933262Ssklower 
45033262Ssklower 	if (intrup) {
45133262Ssklower 	    /* we only get here if there was an interrupt */
45233262Ssklower 
45333262Ssklower 	    fprintf(stderr,"%s: abort (sending)\n",prog);
45433262Ssklower 	    VOIDC fflush(stderr);
45533262Ssklower 
45633262Ssklower 	    /* flush and restart output to printer,
45733262Ssklower 	     * send an abort (^C) request and wait for the job to end
45833262Ssklower 	     */
45933262Ssklower 	    if (ioctl(fdsend, TIOCFLUSH,&flg) || ioctl(fdsend, TIOCSTART,&flg)
46033262Ssklower 	    || (write(fdsend, abortbuf, 1) != 1)) {
46133262Ssklower 		RestoreStatus();
46233262Ssklower 		pexit(prog,THROW_AWAY);
46333262Ssklower 	    }
46433262Ssklower 	    debugp((stderr,"%s: sent interrupt - waiting\n",prog));
46533262Ssklower 	    intrup = 0;
46633262Ssklower 	    goto donefile; /* sorry ewd! */
46733262Ssklower 	}
46833262Ssklower 
46933262Ssklower         VOIDC signal(SIGINT, intsend);
47033262Ssklower         VOIDC signal(SIGHUP, intsend);
47133262Ssklower         VOIDC signal(SIGQUIT, intsend);
47233262Ssklower         VOIDC signal(SIGTERM, intsend);
47333262Ssklower 	VOIDC signal(SIGEMT, readynow);
47433262Ssklower 
47533262Ssklower 	progress = oldprogress = 0; /* finite progress on sender */
47633262Ssklower 	getstatus = FALSE; /* prime the pump for fun FALSE; */
47733262Ssklower 
47833262Ssklower 	VOIDC signal(SIGALRM, salarm); /* sending phase alarm */
47933262Ssklower 	VOIDC alarm(SENDALARM); /* schedule an alarm/timeout */
48033262Ssklower 
48133262Ssklower 	/* loop, trying to send a ^T to get printer status
48233262Ssklower 	 * We will hang here (and post a message) if the printer
48333262Ssklower 	 * is unreachable.  Eventually, we will succeed, the listener
48433262Ssklower 	 * will see the status report, signal us, and we will proceed
48533262Ssklower 	 */
48633262Ssklower 
48733262Ssklower 	cnt = 1;
48833262Ssklower 	VOIDC setjmp(startstatus);
48933262Ssklower 
49033262Ssklower 	while (TRUE) {
49133262Ssklower 	    if (goahead) break;
49233262Ssklower 	    debugp((stderr,"%s: get start status\n",prog));
49333262Ssklower 	    VOIDC write(fdsend, statusbuf, 1);
49433262Ssklower 	    pause();
49533262Ssklower 	    if (goahead) break;
49633262Ssklower 	    /* if we get here, we got an alarm */
49733262Ssklower 	    ioctl(fdsend, TIOCFLUSH, &flg);
49833262Ssklower 	    ioctl(fdsend, TIOCSTART, &flg);
49933262Ssklower 	    sprintf(mybuf, "Not Responding for %d minutes",
50033262Ssklower 	    	(cnt * SENDALARM+30)/60);
50133262Ssklower 	    Status(mybuf);
50233262Ssklower 	    alarm(SENDALARM);
50333262Ssklower 	    cnt++;
50433262Ssklower 	}
50533262Ssklower 
50633262Ssklower 	VOIDC signal(SIGEMT, emtdead); /* now EMTs mean printer died */
50733262Ssklower 
50833262Ssklower 	RestoreStatus();
50933262Ssklower 	debugp((stderr,"%s: printer responding\n",prog));
51033262Ssklower 
51133262Ssklower 	/* initial page accounting (BEFORE break page) */
51233262Ssklower 	if (doactng) {
51333262Ssklower 	    sprintf(mybuf, getpages, "\004");
51433262Ssklower 	    VOIDC write(fdsend, mybuf, strlen(mybuf));
51533263Ssklower 	    debugp((stderr, "%s: sent pagecount request\n", prog));
51633262Ssklower 	    progress++;
51733262Ssklower 
51833263Ssklower             /* Sat Oct 31 17:51:45 PST 1987
51933263Ssklower              * loop, waiting for the listener to signal initial pagecount is
52033263Ssklower              * received.  The problem is with fast machines and short jobs;
52133263Ssklower              * if we don't loop here, we may finish the job and send another
52233263Ssklower              * CTRL-T before the initial pagecount ever came back.  The way
52333263Ssklower              * the laserwriter behaves, this may result in a mix of pagecount
52433263Ssklower              * data and status information like this:
52533263Ssklower              * %%[ pagecount: %%[ status: busy; source: serial 25 ]%% 24418 ]%%
52633263Ssklower              *
52733263Ssklower              * That is really silly - Cuong
52833263Ssklower              */
52933263Ssklower 
53033263Ssklower 	    VOIDC signal(SIGINT, intsend);
53133263Ssklower 	    VOIDC signal(SIGHUP, intsend);
53233263Ssklower 	    VOIDC signal(SIGQUIT, intsend);
53333263Ssklower 	    VOIDC signal(SIGTERM, intsend);
53433263Ssklower 	    VOIDC signal(SIGEMT, readynow);
53533263Ssklower 
53633263Ssklower 	    progress = oldprogress = 0; /* finite progress on sender */
53733263Ssklower 	    getstatus = FALSE; /* prime the pump for fun FALSE; */
53833263Ssklower 
53933263Ssklower 	    VOIDC signal(SIGALRM, salarm); /* sending phase alarm */
54033263Ssklower 	    VOIDC alarm(SENDALARM); /* schedule an alarm/timeout */
54133263Ssklower 
54233263Ssklower 	    cnt = 1; goahead = FALSE;
54333263Ssklower 	    VOIDC setjmp(startstatus);
54433263Ssklower 
54533263Ssklower 	    while (TRUE) {
54633263Ssklower 		if (goahead) break;
54733263Ssklower 		pause();
54833263Ssklower 		if (goahead) break;
54933263Ssklower 		/* if we get here, we got an alarm */
55033263Ssklower 		ioctl(fdsend, TIOCFLUSH, &flg);
55133263Ssklower 		ioctl(fdsend, TIOCSTART, &flg);
55233263Ssklower 		sprintf(mybuf, "Not Responding for %d minutes",
55333263Ssklower 		    (cnt * SENDALARM+30)/60);
55433263Ssklower 		Status(mybuf);
55533263Ssklower 		alarm(SENDALARM);
55633263Ssklower 		cnt++;
55733263Ssklower 	    }
55833263Ssklower 
55933263Ssklower 	    VOIDC signal(SIGEMT, emtdead); /* now EMTs mean printer died */
56033263Ssklower 
56133263Ssklower 	    RestoreStatus();
56233263Ssklower 	    debugp((stderr,"%s: sender received EMT (goahead) from listener\n",prog));
56333263Ssklower         } /* if (doactng) */
56433263Ssklower 
56533262Ssklower 	/* initial break page ? */
56633262Ssklower 	if (BannerFirst) {
56733262Ssklower 	    SendBanner();
56833262Ssklower 	    progress++;
56933262Ssklower 	}
57033262Ssklower 
57133262Ssklower 	/* ship the magic number! */
57233262Ssklower 	if ((!format) && (!reversing)) {
57333262Ssklower 	   VOIDC write(fdsend,magic,11);
57433262Ssklower 	   progress++;
57533262Ssklower 	}
57633262Ssklower 
57733262Ssklower 	/* now ship the rest of the file */
57833262Ssklower 
57933262Ssklower 	VOIDC alarm(SENDALARM); /* schedule an alarm */
58033262Ssklower 
58133262Ssklower 	while ((cnt = read(fdinput, mybuf, sizeof mybuf)) > 0) {
58233262Ssklower 	    /* VOIDC alarm(SENDALARM);	/* we made progress, reset alarm */
58333262Ssklower 	    if (intrup == TRUE) break;
58433262Ssklower 
58533262Ssklower 	    /* get status every other time */
58633262Ssklower 	    if (getstatus) {
58733263Ssklower 		debugp((stderr,"%s: get periodic status\n",prog));
58833262Ssklower 		VOIDC write(fdsend, statusbuf, 1);
58933262Ssklower 		getstatus = FALSE;
59033262Ssklower 		progress++;
59133262Ssklower 	    }
59233262Ssklower 	    mbp = mybuf;
59333262Ssklower 	    while ((cnt > 0) && ((wc = write(fdsend, mbp, cnt)) != cnt)) {
59433262Ssklower 		/* this seems necessary but not sure why */
59533262Ssklower 		if (wc < 0) {
59633262Ssklower 		    fprintf(stderr,"%s: error writing to printer:\n",prog);
59733262Ssklower 		    perror(prog);
59833262Ssklower 		    RestoreStatus();
59933262Ssklower 		    sleep(10);
60033262Ssklower 		    exit(TRY_AGAIN);
60133262Ssklower 		}
60233262Ssklower 		mbp += wc;
60333262Ssklower 		cnt -= wc;
60433262Ssklower 		progress++;
60533262Ssklower 	    }
60633262Ssklower 	    progress++;
60733262Ssklower 	}
60833262Ssklower 	if (cnt < 0) {
60933262Ssklower 	    fprintf(stderr,"%s: error reading from stdin: \n", prog);
61033262Ssklower 	    perror(prog);
61133262Ssklower 	    RestoreStatus();
61233262Ssklower 	    sleep(10);
61333262Ssklower 	    exit(TRY_AGAIN);	/* kill the listener? */
61433262Ssklower 	}
61533262Ssklower 
61633263Ssklower 	/* final break page ? */
61733263Ssklower 	if (BannerLast) {
61833263Ssklower 	    SendBanner();
61933263Ssklower 	    progress++;
62033263Ssklower 	}
62133262Ssklower 
62233262Ssklower 	donefile:;
62333262Ssklower 
62433262Ssklower 	sendend = 1;
62533262Ssklower 
62633262Ssklower 	VOIDC setjmp(dwait);
62733262Ssklower 
62833262Ssklower 	if (sendend && !gotemt) {
62933262Ssklower 
63033262Ssklower 	    VOIDC signal(SIGEMT, emtdone);
63133262Ssklower 
63233262Ssklower 	    debugp((stderr,"%s: done sending\n",prog));
63333262Ssklower 
63433262Ssklower 	    /* now send the PostScript EOF character */
63533263Ssklower 	    debugp((stderr,"%s: sending PostScript EOF\n",prog));
63633262Ssklower 	    VOIDC write(fdsend, eofbuf, 1);
63733262Ssklower 	    sendend = 0;
63833262Ssklower 	    progress++;
63933262Ssklower 
64033262Ssklower 	    VOIDC signal(SIGINT, intwait);
64133262Ssklower 	    VOIDC signal(SIGHUP, intwait);
64233262Ssklower 	    VOIDC signal(SIGQUIT, intwait);
64333262Ssklower 	    VOIDC signal(SIGTERM, intwait);
64433262Ssklower 
64533262Ssklower 	    VOIDC signal(SIGALRM, walarm);
64633262Ssklower 	    VOIDC alarm(WAITALARM);
64733262Ssklower 	    getstatus = TRUE;
64833262Ssklower 	}
64933262Ssklower 
65033263Ssklower 	/* for very short jobs and very fast machines,
65133263Ssklower 	 * we've experienced that the whole job is sent
65233263Ssklower 	 * before the LaserWriter has a chance to update
65333263Ssklower 	 * its status.  Hence we may get a false idle
65433263Ssklower 	 * status if we immediately send the statusbuf.
65533263Ssklower 	 *
65633263Ssklower 	 * Keep in mind that the LaserWriter status response
65733263Ssklower 	 * is asynchronous to the datastream.
65833263Ssklower 	 */
65933263Ssklower 	sleep(1);
66033263Ssklower 
66133262Ssklower 	/* wait to sync with listener EMT signal
66233262Ssklower 	 * to indicate it got an EOF from the printer
66333262Ssklower 	 */
66433262Ssklower 	while (TRUE) {
66533262Ssklower 	    if (gotemt) break;
66633262Ssklower 	    if (getstatus) {
66733263Ssklower 		debugp((stderr,"%s: get final status\n",prog));
66833262Ssklower 		VOIDC write(fdsend, statusbuf, 1);
66933262Ssklower 		getstatus = FALSE;
67033262Ssklower 	    }
67133262Ssklower 	    debugp((stderr,"waiting e%d i%d %d %d\n",
67233262Ssklower 	    	gotemt,intrup,wpid,status));
67333262Ssklower 	    wpid = wait(&status);
67433262Ssklower 	    if (wpid == -1) break;
67533262Ssklower 	}
67633262Ssklower 
67733262Ssklower 	/* final page accounting */
67833262Ssklower 	if (doactng) {
67933263Ssklower 	    sprintf(mybuf, getpages, "\004");
68033262Ssklower 	    VOIDC write(fdsend, mybuf, strlen(mybuf));
68133263Ssklower 	    debugp((stderr, "%s: sent pagecount request\n", prog));
68233262Ssklower 	    progress++;
68333262Ssklower         }
68433262Ssklower 
68533262Ssklower 	/* wait for listener to die */
68633262Ssklower 	VOIDC setjmp(dwait);
68733262Ssklower         while ((wpid = wait(&status)) > 0);
68833262Ssklower 	VOIDC alarm(0);
68933262Ssklower 	VOIDC signal(SIGINT, SIG_IGN);
69033262Ssklower 	VOIDC signal(SIGHUP, SIG_IGN);
69133262Ssklower 	VOIDC signal(SIGQUIT, SIG_IGN);
69233262Ssklower 	VOIDC signal(SIGTERM, SIG_IGN);
69333262Ssklower 	VOIDC signal(SIGEMT, SIG_IGN);
69433262Ssklower 	debugp((stderr,"w2: s%lo p%d = p%d\n", status, wpid, cpid));
69533262Ssklower 
69633262Ssklower 	if (VerboseLog) {
69733262Ssklower 	    fprintf(stderr,"%s: end - %s",prog,
69833262Ssklower 	    	(VOIDC time(&clock),ctime(&clock)));
69933262Ssklower 	    VOIDC fflush(stderr);
70033262Ssklower 	}
70133262Ssklower     RestoreStatus();
70233262Ssklower     exit(0);
70333262Ssklower     }
70433262Ssklower     else {/* child - listener */
70533262Ssklower       register FILE *psin;
70633262Ssklower       register int r;
70733262Ssklower 
70833262Ssklower       char pbuf[BUFSIZ]; /* buffer for pagecount info */
70933262Ssklower       char *pb;		/* pointer for above */
71033262Ssklower       int pc1, pc2; 	/* page counts before and after job */
71133262Ssklower       int sc;		/* pattern match count for sscanf */
71233262Ssklower       char *outname;	/* file name for job output */
71333262Ssklower       int havejobout = FALSE; /* flag if jobout != stderr */
71433262Ssklower       int ppid;		/* parent process id */
71533262Ssklower 
71633262Ssklower       VOIDC signal(SIGINT, SIG_IGN);
71733262Ssklower       VOIDC signal(SIGHUP, SIG_IGN);
71833262Ssklower       VOIDC signal(SIGQUIT, SIG_IGN);
71933262Ssklower       VOIDC signal(SIGTERM, SIG_IGN);
72033262Ssklower       VOIDC signal(SIGALRM, SIG_IGN);
72133262Ssklower 
72233262Ssklower       ppid = getppid();
72333262Ssklower 
72433262Ssklower       /* get jobout from environment if there, otherwise use stderr */
72533262Ssklower       if (((outname = envget("JOBOUTPUT")) == NULL)
72633262Ssklower       || ((jobout = fopen(outname,"w")) == NULL)) {
72733262Ssklower 	  jobout = stderr;
72833262Ssklower       }
72933262Ssklower       else havejobout = TRUE;
73033262Ssklower 
73133262Ssklower       pc1 = pc2 = -1; /* bogus initial values */
73233262Ssklower       if ((psin = fdopen(fdlisten, "r")) == NULL) {
73333262Ssklower 	  RestoreStatus();
73433262Ssklower 	  pexit(prog, THROW_AWAY);
73533262Ssklower       }
73633262Ssklower 
73733262Ssklower       /* listen for first status (idle?) */
73833262Ssklower       pb = pbuf;
73933262Ssklower       *pb = '\0';
74033262Ssklower       while (TRUE) {
74133262Ssklower 	  r = getc(psin);
74233262Ssklower 	  if (r == EOF) {
74333262Ssklower 	      fprintf(stderr, EOFerr, prog, "startup");
74433262Ssklower 	      VOIDC fflush(stderr);
74533262Ssklower 	      sleep(20); /* printer may be coming up */
74633262Ssklower 	      /* RestoreStatus(); */
74733262Ssklower 	      /* exit(TRY_AGAIN); */
74833262Ssklower 	  }
74933262Ssklower 	  if ((r & 0377) == '\n') break; /* newline */
75033262Ssklower 	  *pb++ = r;
75133262Ssklower       }
75233262Ssklower       *pb = 0;
75333263Ssklower       debugp((stderr,"%s: initial status - %s\n",prog,pbuf));
75433262Ssklower       if (strcmp(pbuf, "%%[ status: idle ]%%\r") != 0) {
75533262Ssklower 	  fprintf(stderr,"%s: initial status - %s\n",prog,pbuf);
75633262Ssklower 	  VOIDC fflush(stderr);
75733262Ssklower       }
75833262Ssklower 
75933262Ssklower       /* flush input state and signal sender that we heard something */
76033262Ssklower       ioctl(fdlisten, TIOCFLUSH, &flg);
76133262Ssklower 
76233262Ssklower       VOIDC kill(ppid,SIGEMT);
76333262Ssklower 
76433262Ssklower       /* listen for first pagecount */
76533262Ssklower       if (doactng) {
76633262Ssklower         pb = pbuf;
76733262Ssklower 	*pb = '\0';
76833262Ssklower 	while (TRUE) {
76933262Ssklower 	  r = getc(psin);
77033262Ssklower 	  if (r == EOF) {
77133262Ssklower 	      fprintf(stderr, EOFerr, prog, "accounting1");
77233262Ssklower 	      VOIDC fflush(stderr);
77333262Ssklower 	      RestoreStatus();
77433262Ssklower 	      sleep(10);	/* give interface a chance */
77533262Ssklower 	      exit(TRY_AGAIN);
77633262Ssklower 	  }
77733262Ssklower 	  if ((r&0377) == 004) break; /* PS_EOF */
77833262Ssklower 	  *pb++ = r;
77933262Ssklower 	}
78033262Ssklower 	*pb = '\0';
78133262Ssklower 
78233262Ssklower 	if (pb = FindPattern(pb, pbuf, "%%[ pagecount: ")) {
78333262Ssklower 	    sc = sscanf(pb, "%%%%[ pagecount: %d ]%%%%\r", &pc1);
78433262Ssklower 	}
78533262Ssklower 	if ((pb == NULL) || (sc != 1)) {
78633262Ssklower 	    fprintf(stderr, "%s: accounting error 1 (%s)\n", prog,pbuf);
78733262Ssklower 	    VOIDC fflush(stderr);
78833262Ssklower 	}
78933262Ssklower 	debugp((stderr,"%s: accounting 1 (%s)\n",prog,pbuf));
79033263Ssklower 
79133263Ssklower         /* flush input state and signal sender that we heard something */
79233263Ssklower         ioctl(fdlisten, TIOCFLUSH, &flg);
79333263Ssklower 
79433263Ssklower         VOIDC kill(ppid,SIGEMT);
79533263Ssklower 
79633263Ssklower 	/*
79733263Ssklower 	    Sun Sep 20 18:32:28 PDT 1987
79833263Ssklower 	    The previous bug was that it was assumed the ctrl-d comes
79933263Ssklower 	    before the final pagecount.  This doesn't happen, and the
80033263Ssklower 	    listener waits forever after a ctrl-d for a pagecount.
80133263Ssklower 	    The fix is to clear out the pbuf[] buffer, then check for it
80233263Ssklower 	    when we get to looking for the final pagecount.  If it is
80333263Ssklower 	    non-empty, we know we *already* read the final pagecount
80433263Ssklower 	    *before* the ctrl-d, and use it, without waiting for
80533263Ssklower 	    anything to come back from the printer.
80633263Ssklower 	*/
80733263Ssklower 	pbuf[0] = '\0';
80833262Ssklower       }
80933262Ssklower 
81033262Ssklower       /* listen for the user job */
81133262Ssklower       while (TRUE) {
81233262Ssklower 	r = getc(psin);
81333263Ssklower 	debugp((stderr, "%s: listener got character \\%o '%c'\n", prog, r, r));
81433262Ssklower 	if ((r&0377) == 004) break; /* PS_EOF */
81533262Ssklower 	else if (r == EOF) {
81633262Ssklower 	    VOIDC fclose(psin);
81733262Ssklower 	    fprintf(stderr, EOFerr, prog, "job");
81833262Ssklower 	    VOIDC fflush(stderr);
81933262Ssklower 	    RestoreStatus();
82033262Ssklower 	    VOIDC kill(ppid,SIGEMT);
82133262Ssklower 	    exit(THROW_AWAY);
82233262Ssklower 	}
82333263Ssklower /*
82433263Ssklower     Sun Sep 20 18:37:01 PDT 1987
82533263Ssklower     GotChar() takes an addition argument: the pointer to the
82633263Ssklower     pbuf[] buffer, and fills it with the final pagecount
82733263Ssklower     information if that is received from the printer.
82833263Ssklower */
82933263Ssklower 	GotChar(r, pbuf);
83033262Ssklower       }
83133262Ssklower 
83233262Ssklower       /* let sender know we saw the end of the job */
83333262Ssklower       /* sync - wait for sender to restart us */
83433262Ssklower 
83533262Ssklower       debugp((stderr,"%s: listener saw eof, signaling\n",prog));
83633262Ssklower 
83733262Ssklower       VOIDC kill(ppid,SIGEMT);
83833262Ssklower 
83933262Ssklower       /* now get final page count */
84033262Ssklower       if (doactng) {
84133263Ssklower /*
84233263Ssklower     Sun Sep 20 18:48:35 PDT 1987
84333263Ssklower     We attempt to wait for the final pagecount only if it has *not*
84433263Ssklower     been sent by the printer.  It is the case that the final pagecount
84533263Ssklower     is sent before the ctrl-d above, hence if we wait, it'll be forever.
84633263Ssklower     Final pagecount information 'prematurely' received has already
84733263Ssklower     been stored in pbuf[] iff pbuf[0] is non-null.
84833263Ssklower */
84933263Ssklower 
85033263Ssklower 	if (pbuf[0] == '\0') {
85133263Ssklower 	    debugp((stderr, "%s: waiting for pagecount\n", prog));
85233263Ssklower 	    pb = pbuf;
85333263Ssklower 	    *pb = '\0';	/* ignore the previous pagecount */
85433263Ssklower 	    while (TRUE) {
85533263Ssklower 	      r = getc(psin);
85633263Ssklower 	      if (r == EOF) {
85733263Ssklower 		  fprintf(stderr, EOFerr, prog, "accounting2");
85833263Ssklower 		  VOIDC fflush(stderr);
85933263Ssklower 		  RestoreStatus();
86033263Ssklower 		  sleep(10);
86133263Ssklower 		  exit(THROW_AWAY); /* what else to do? */
86233263Ssklower 	      }
86333263Ssklower 	      if ((r&0377) == 004) break; /* PS_EOF */
86433263Ssklower 	      *pb++ = r;
86533263Ssklower 	    }
86633263Ssklower 	    *pb = '\0';
86733263Ssklower 	} else {
86833263Ssklower 	    pb = pbuf + strlen(pbuf) - 1;
86933262Ssklower 	}
87033262Ssklower 	debugp((stderr,"%s: accounting 2 (%s)\n",prog,pbuf));
87133262Ssklower 	if (pb = FindPattern(pb, pbuf, "%%[ pagecount: ")) {
87233262Ssklower 	    sc = sscanf(pb, "%%%%[ pagecount: %d ]%%%%\r", &pc2);
87333262Ssklower 	}
87433262Ssklower 	if ((pb == NULL) || (sc != 1)) {
87533262Ssklower 	    fprintf(stderr, "%s: accounting error 2 (%s)\n", prog,pbuf);
87633262Ssklower 	    VOIDC fflush(stderr);
87733262Ssklower 	}
87833262Ssklower         else if ((pc2 < pc1) || (pc1 < 0) || (pc2 < 0)) {
87933262Ssklower 	    fprintf(stderr,"%s: accounting error 3 %d %d\n", prog,pc1,pc2);
88033262Ssklower 	    VOIDC fflush(stderr);
88133262Ssklower 	}
88233262Ssklower 	else if (freopen(accountingfile, "a", stdout) != NULL) {
88333262Ssklower 	  printf("%7.2f\t%s:%s\n", (float)(pc2 - pc1), host, name);
88433262Ssklower 	  VOIDC fclose(stdout);
88533263Ssklower /*
88633263Ssklower     Sun Sep 20 18:55:32 PDT 1987
88733263Ssklower     File append failure report added for future use.
88833263Ssklower */
88933263Ssklower 	} else {
89033263Ssklower 	  debugp((stderr, "%s: can't append accounting file\n", prog));
89133263Ssklower 	  perror(accountingfile);
89233262Ssklower 	}
89333262Ssklower       }
89433262Ssklower 
89533262Ssklower       /* all done -- let sender know */
89633262Ssklower       if (havejobout) VOIDC fclose(jobout);
89733262Ssklower       VOIDC fclose(psin);
89833262Ssklower       exit(0); /* to parent */
89933262Ssklower     }
90033262Ssklower }
90133262Ssklower 
90233262Ssklower /* send the file ".banner" */
90333262Ssklower private SendBanner()
90433262Ssklower {
90533262Ssklower     register int banner;
90633262Ssklower     int cnt;
90733262Ssklower     char buf[BUFSIZ];
90833262Ssklower 
90933262Ssklower     if ((banner = open(".banner",O_RDONLY|O_NDELAY,0)) < 0) return;
91033262Ssklower     while ((cnt = read(banner,buf,sizeof buf)) > 0) {
91133262Ssklower 	VOIDC write(fdsend,buf,cnt);
91233262Ssklower     }
91333262Ssklower     VOIDC close(banner);
914*33796Skarels     VOIDC unlink(".banner");
91533262Ssklower }
91633262Ssklower 
91733262Ssklower /* search backwards from p in start for patt */
91833262Ssklower private char *FindPattern(p, start, patt)
91933262Ssklower char *p;
92033262Ssklower char *start;
92133262Ssklower char *patt;
92233262Ssklower {
92333262Ssklower     int patlen;
92433262Ssklower     patlen = strlen(patt);
92533262Ssklower 
92633262Ssklower     p -= patlen;
92733262Ssklower     for (; p >= start; p--) {
92833262Ssklower 	if (strncmp(p, patt, patlen) == 0) return(p);
92933262Ssklower     }
93033262Ssklower     return ((char *)NULL);
93133262Ssklower }
93233262Ssklower 
93333263Ssklower private GotChar(c, pbuf)
93433262Ssklower register int c;
93533263Ssklower char	*pbuf;
93633262Ssklower {
93733262Ssklower     static char linebuf[BUFSIZ];
93833262Ssklower     static char *cp = linebuf;
93933262Ssklower     static enum State {normal, onep, twop, inmessage,
94033262Ssklower     			close1, close2, close3, close4} st = normal;
94133262Ssklower     char *match, *last;
94233262Ssklower 
94333262Ssklower     switch (st) {
94433262Ssklower 	case normal:
94533262Ssklower 	    if (c == '%') {
94633262Ssklower 		st = onep;
94733262Ssklower 		cp = linebuf;
94833262Ssklower 		*cp++ = c;
94933262Ssklower 		break;
95033262Ssklower 	    }
95133262Ssklower 	    putc(c,jobout);
95233262Ssklower 	    VOIDC fflush(jobout);
95333262Ssklower 	    break;
95433262Ssklower 	case onep:
95533262Ssklower 	    if (c == '%') {
95633262Ssklower 		st = twop;
95733262Ssklower 		*cp++ = c;
95833262Ssklower 		break;
95933262Ssklower 	    }
96033262Ssklower 	    putc('%',jobout);
96133262Ssklower 	    putc(c,jobout);
96233262Ssklower 	    VOIDC fflush(jobout);
96333262Ssklower 	    st = normal;
96433262Ssklower 	    break;
96533262Ssklower 	case twop:
96633262Ssklower 	    if (c == '\[') {
96733262Ssklower 		st = inmessage;
96833262Ssklower 		*cp++ = c;
96933262Ssklower 		break;
97033262Ssklower 	    }
97133262Ssklower 	    if (c == '\%') {
97233262Ssklower 		putc('%',jobout);
97333262Ssklower 		VOIDC fflush(jobout);
97433262Ssklower 		/* don't do anything to cp */
97533262Ssklower 		break;
97633262Ssklower 	    }
97733262Ssklower 	    putc('%',jobout);
97833262Ssklower 	    putc('%',jobout);
97933262Ssklower 	    VOIDC fflush(jobout);
98033262Ssklower 	    st = normal;
98133262Ssklower 	    break;
98233262Ssklower 	case inmessage:
98333262Ssklower 	    *cp++ = c;
98433262Ssklower 	    if (c == '\]') st = close1;
98533262Ssklower 	    break;
98633262Ssklower 	case close1:
98733262Ssklower 	    *cp++ = c;
98833262Ssklower 	    switch (c) {
98933262Ssklower 		case '%': st = close2; break;
99033262Ssklower 		case '\]': st = close1; break;
99133262Ssklower 		default: st = inmessage; break;
99233262Ssklower 	    }
99333262Ssklower 	    break;
99433262Ssklower 	case close2:
99533262Ssklower 	    *cp++ = c;
99633262Ssklower 	    switch (c) {
99733262Ssklower 		case '%': st = close3; break;
99833262Ssklower 		case '\]': st = close1; break;
99933262Ssklower 		default: st = inmessage; break;
100033262Ssklower 	    }
100133262Ssklower 	    break;
100233262Ssklower 	case close3:
100333262Ssklower 	    *cp++ = c;
100433262Ssklower 	    switch (c) {
100533262Ssklower 		case '\r': st = close4; break;
100633262Ssklower 		case '\]': st = close1; break;
100733262Ssklower 		default: st = inmessage; break;
100833262Ssklower 	    }
100933262Ssklower 	    break;
101033262Ssklower 	case close4:
101133262Ssklower 	    *cp++ = c;
101233262Ssklower 	    switch(c) {
101333262Ssklower 		case '\n': st = normal; break;
101433262Ssklower 		case '\]': st = close1; break;
101533262Ssklower 		default: st = inmessage; break;
101633262Ssklower 	    }
101733262Ssklower 	    if (st == normal) {
101833262Ssklower 		/* parse complete message */
101933262Ssklower 		last = cp;
102033262Ssklower 		*cp = 0;
102133262Ssklower 		debugp((stderr,">>%s",linebuf));
102233262Ssklower 		if (match = FindPattern(cp, linebuf, " PrinterError: ")) {
102333262Ssklower 		    if (*(match-1) != ':') {
102433262Ssklower 			fprintf(stderr,"%s",linebuf);
102533262Ssklower 			VOIDC fflush(stderr);
102633262Ssklower 			*(last-6) = 0;
102733262Ssklower 			Status(match+15);
102833262Ssklower 		    }
102933262Ssklower 		    else {
103033262Ssklower 			last = index(match,';');
103133262Ssklower 			*last = 0;
103233262Ssklower 			Status(match+15);
103333262Ssklower 		    }
103433262Ssklower 		}
103533262Ssklower 		else if (match = FindPattern(cp, linebuf, " status: ")) {
103633262Ssklower 		    match += 9;
103733262Ssklower 		    if (strncmp(match,"idle",4) == 0) {
103833262Ssklower 			/* we are hopelessly lost, get everyone to quit */
103933262Ssklower 			fprintf(stderr,"%s: ERROR: printer is idle, giving up!\n",prog);
104033262Ssklower 			VOIDC fflush(stderr);
104133262Ssklower 			VOIDC kill(getppid(),SIGKILL); /* will this work */
104233262Ssklower 			exit(THROW_AWAY);
104333262Ssklower 		    }
104433262Ssklower 		    else {
104533262Ssklower 			/* one of: busy, waiting, printing, initializing */
104633262Ssklower 			/* clear status message */
104733262Ssklower 			RestoreStatus();
104833262Ssklower 		    }
104933262Ssklower 		}
105033263Ssklower /*
105133263Ssklower     Sun Sep 20 18:39:40 PDT 1987
105233263Ssklower     Additional else necessary: if we get the final pagecount
105333263Ssklower     information here from the printer, store it in the given
105433263Ssklower     array pbuf[].
105533263Ssklower */
105633263Ssklower 		else if (match = FindPattern(cp, linebuf, "%%[ pagecount: ")) {
105733263Ssklower 		    /* fill pbuf */
105833263Ssklower 		    strcpy(pbuf, linebuf);
105933263Ssklower 		    debugp((stderr, "%s: 'premature' final pagecount read = '%s'\n", prog, pbuf));
106033263Ssklower 		}
106133262Ssklower 		else {
106233262Ssklower 		    /* message not for us */
106333262Ssklower 		    fprintf(jobout,"%s",linebuf);
106433262Ssklower 		    VOIDC fflush(jobout);
106533262Ssklower 		    st = normal;
106633262Ssklower 		    break;
106733262Ssklower 		}
106833262Ssklower 	    }
106933262Ssklower 	    break;
107033262Ssklower 	default:
107133262Ssklower 	    fprintf(stderr,"bad case;\n");
107233262Ssklower     }
107333262Ssklower     return;
107433262Ssklower }
107533262Ssklower 
107633262Ssklower /* backup "status" message file in ".status",
107733262Ssklower  * in case there is a PrinterError
107833262Ssklower  */
107933262Ssklower 
108033262Ssklower private BackupStatus(file1, file2)
108133262Ssklower char *file1, *file2;
108233262Ssklower {
108333262Ssklower     register int fd1, fd2;
108433262Ssklower     char buf[BUFSIZ];
108533262Ssklower     int cnt;
108633262Ssklower 
108733262Ssklower     VOIDC umask(0);
108833262Ssklower     fd1 = open(file1, O_WRONLY|O_CREAT, 0664);
108933262Ssklower     if ((fd1 < 0) || (flock(fd1,LOCK_EX) < 0)) {
109033262Ssklower 	VOIDC unlink(file1);
109133262Ssklower 	VOIDC flock(fd1,LOCK_UN);
109233262Ssklower 	VOIDC close(fd1);
109333262Ssklower 	fd1 = open(file1, O_WRONLY|O_CREAT, 0664);
109433262Ssklower     }
109533262Ssklower     if ((fd1 < 0) || (flock(fd1,LOCK_EX) <0)) {
109633262Ssklower 	fprintf(stderr, "%s: writing %s:\n",prog,file1);
109733262Ssklower 	perror(prog);
109833262Ssklower 	VOIDC close(fd1);
109933262Ssklower 	return;
110033262Ssklower     }
110133262Ssklower     VOIDC ftruncate(fd1,0);
110233262Ssklower     if ((fd2 = open(file2, O_RDONLY,0)) < 0) {
110333262Ssklower 	fprintf(stderr, "%s: error reading %s:\n", prog, file2);
110433262Ssklower 	perror(prog);
110533262Ssklower 	VOIDC close(fd1);
110633262Ssklower 	return;
110733262Ssklower     }
110833262Ssklower     cnt = read(fd2,buf,BUFSIZ);
110933262Ssklower     VOIDC write(fd1,buf,cnt);
111033262Ssklower     VOIDC flock(fd1,LOCK_UN);
111133262Ssklower     VOIDC close(fd1);
111233262Ssklower     VOIDC close(fd2);
111333262Ssklower }
111433262Ssklower 
111533262Ssklower /* restore the "status" message from the backed-up ".status" copy */
111633262Ssklower private RestoreStatus() {
111733262Ssklower     BackupStatus("status",".status");
111833262Ssklower }
111933262Ssklower 
112033262Ssklower /* report PrinterError via "status" message file */
112133262Ssklower private Status(msg)
112233262Ssklower register char *msg;
112333262Ssklower {
112433262Ssklower     register int fd;
112533262Ssklower     char msgbuf[100];
112633262Ssklower 
112733262Ssklower     if ((fd = open("status",O_WRONLY|O_CREAT,0664)) < 0) return;
112833262Ssklower     VOIDC ftruncate(fd,0);
112933262Ssklower     sprintf(msgbuf,"Printer Error: may need attention! (%s)\n\0",msg);
113033262Ssklower     VOIDC write(fd,msgbuf,strlen(msgbuf));
113133262Ssklower     VOIDC close(fd);
113233262Ssklower }
113333262Ssklower 
113433262Ssklower /* sending phase alarm handler for sender */
113533262Ssklower 
113633262Ssklower private VOID salarm() {
113733262Ssklower 
113833262Ssklower     debugp((stderr,"%s: AS %d %d %d\n",prog,oldprogress,progress,getstatus));
113933262Ssklower 
114033262Ssklower     /* if progress != oldprogress, we made some progress (sent something)
114133262Ssklower      * else, we had two alarms without sending anything...
114233262Ssklower      * It may be that a PrinterError has us stopped, or we are computing
114333262Ssklower      * for a long time (forever?) -- printer jobtimeout may help here
114433262Ssklower      * in any case, all we do is set the flag to get status...
114533262Ssklower      * this will help us clear printererror notification
114633262Ssklower      */
114733262Ssklower 
114833262Ssklower     oldprogress = progress;
114933262Ssklower     getstatus = TRUE;
115033262Ssklower 
115133262Ssklower     /* reset the alarm and return */
115233262Ssklower     VOIDC alarm(SENDALARM);
115333262Ssklower     return;
115433262Ssklower }
115533262Ssklower 
115633262Ssklower /* waiting phase alarm handler for sender */
115733262Ssklower 
115833262Ssklower private VOID walarm() {
115933262Ssklower     static int acount = 0;
116033262Ssklower 
116133262Ssklower     debugp((stderr,"%s: WA %d %d %d %d\n",
116233262Ssklower     	prog,acount,oldprogress,progress,getstatus));
116333262Ssklower 
116433262Ssklower     if ((oldprogress != progress) || (acount == 4)) {
116533262Ssklower 	getstatus = TRUE;
116633262Ssklower 	acount = 0;
116733262Ssklower 	oldprogress = progress;
116833262Ssklower     }
116933262Ssklower     else acount++;
117033262Ssklower 
117133262Ssklower     /* reset alarm */
117233262Ssklower     VOIDC alarm(WAITALARM);
117333262Ssklower 
117433262Ssklower     /* return to wait loop */
117533262Ssklower     longjmp(dwait, 0);
117633262Ssklower }
117733262Ssklower 
117833262Ssklower /* final phase alarm handler for sender */
117933262Ssklower 
118033262Ssklower private VOID falarm() {
118133262Ssklower 
118233262Ssklower     debugp((stderr,"%s: FA %d %d %d\n",prog,oldprogress,progress,getstatus));
118333262Ssklower 
118433262Ssklower     /* no reason to count progress, just get status */
118533262Ssklower     if (!intrup) {
118633262Ssklower 	VOIDC write(fdsend, statusbuf, 1);
118733262Ssklower     }
118833262Ssklower     getstatus = FALSE;
118933262Ssklower 
119033262Ssklower     /* reset alarm */
119133262Ssklower     VOIDC alarm(WAITALARM);
119233262Ssklower     return;
119333262Ssklower }
119433262Ssklower 
119533262Ssklower /* initial interrupt handler - before communications begin, so
119633262Ssklower  * nothing to be sent to printer
119733262Ssklower  */
119833262Ssklower private VOID intinit() {
119933262Ssklower     long clock;
120033262Ssklower 
120133262Ssklower     /* get rid of banner file */
120233262Ssklower     VOIDC unlink(".banner");
120333262Ssklower 
120433262Ssklower     fprintf(stderr,"%s: abort (during setup)\n",prog);
120533262Ssklower     VOIDC fflush(stderr);
120633262Ssklower 
120733262Ssklower     /* these next two may be too cautious */
120833262Ssklower     VOIDC kill(0,SIGINT);
120933262Ssklower     while (wait((union wait *) 0) > 0);
121033262Ssklower 
121133262Ssklower     if (VerboseLog) {
121233262Ssklower 	fprintf (stderr, "%s: end - %s", prog, (time(&clock), ctime(&clock)));
121333262Ssklower 	VOIDC fflush(stderr);
121433262Ssklower     }
121533262Ssklower 
121633262Ssklower     exit(THROW_AWAY);
121733262Ssklower }
121833262Ssklower 
121933262Ssklower /* interrupt during sending phase to sender process */
122033262Ssklower 
122133262Ssklower private VOID intsend() {
122233262Ssklower     /* set flag */
122333262Ssklower     intrup = TRUE;
122433262Ssklower     longjmp(sendint, 0);
122533262Ssklower }
122633262Ssklower 
122733262Ssklower /* interrupt during waiting phase to sender process */
122833262Ssklower 
122933262Ssklower private VOID intwait() {
123033262Ssklower 
123133262Ssklower     intrup = TRUE;
123233262Ssklower 
123333262Ssklower     fprintf(stderr,"%s: abort (waiting)\n",prog);
123433262Ssklower     VOIDC fflush(stderr);
123533262Ssklower     if (ioctl(fdsend, TIOCFLUSH, &flg) || ioctl(fdsend, TIOCSTART, &flg)
123633262Ssklower     || (write(fdsend, abortbuf, 1) != 1)) {
123733262Ssklower 	fprintf(stderr, "%s: error in ioctl(fdsend):\n", prog);
123833262Ssklower 	perror(prog);
123933262Ssklower     }
124033262Ssklower 
124133262Ssklower     /* VOIDC alarm(2); /* force an alarm soon to get us out of wait! ? */
124233262Ssklower     longjmp(dwait, 0);
124333262Ssklower }
124433262Ssklower 
124533262Ssklower /* EMT for reverse filter, avoid printer timeout at the expense
124633262Ssklower  * of performance (sigh)
124733262Ssklower  */
124833262Ssklower 
124933262Ssklower private VOID reverseready() {
125033262Ssklower     revdone = TRUE;
125133262Ssklower     longjmp(waitonreverse, 0);
125233262Ssklower }
125333262Ssklower 
125433262Ssklower /* EMT on startup to sender -- signalled by listener after first status
125533262Ssklower  * message received
125633262Ssklower  */
125733262Ssklower 
125833262Ssklower private VOID readynow() {
125933262Ssklower     goahead = TRUE;
126033262Ssklower     longjmp(startstatus, 0);
126133262Ssklower }
126233262Ssklower 
126333262Ssklower /* EMT on sending phase, hard EOF printer died! */
126433262Ssklower private VOID emtdead() {
126533262Ssklower     VOIDC alarm(0);
126633262Ssklower     exit(THROW_AWAY);
126733262Ssklower }
126833262Ssklower 
126933262Ssklower /* EMT during waiting phase -- listener saw an EOF (^D) from printer */
127033262Ssklower 
127133262Ssklower private VOID emtdone() {
127233262Ssklower     VOIDC alarm(0);
127333262Ssklower     gotemt = TRUE;
127433262Ssklower     longjmp(dwait, 0);
127533262Ssklower }
1276