xref: /csrg-svn/local/transcript/src/pscomm.c (revision 64050)
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 *reverse;
15933262Ssklower private int BannerFirst;
16033262Ssklower private int BannerLast;
16133262Ssklower private int VerboseLog;
16233262Ssklower 
16333262Ssklower private int	fpid = 0;	/* formatter pid */
16433262Ssklower private int	cpid = 0;	/* listener pid */
16533262Ssklower 
16633262Ssklower private int	intrup = FALSE;	/* interrupt flag */
16733262Ssklower 
16834945Sedward private char abortbuf[] = {PS_INT, 0};		/* ^C abort */
16934945Sedward private char statusbuf[] = {PS_STATUS, 0};	/* ^T status */
17034945Sedward private char eofbuf[] = {PS_EOF, 0};		/* ^D end of file */
17133262Ssklower 
17233262Ssklower private char EOFerr[] = "%s: unexpected EOF from printer (%s)!\n";
17333262Ssklower 
17433262Ssklower /* global file descriptors (avoid stdio buffering!) */
17533262Ssklower private int fdsend;		/* to printer (from stdout) */
17633262Ssklower private int fdlisten;		/* from printer (same tty line) */
17733262Ssklower private int fdinput;		/* file to print (from stdin) */
17833262Ssklower 
17933262Ssklower private FILE *jobout;		/* special printer output log */
18033262Ssklower 
181*64050Sbostic private int flg = 3/*FREAD|FWRITE*/;	 /* ioctl FLUSH arg */
18233262Ssklower 
18333262Ssklower private VOID	intinit();
18433262Ssklower private VOID	intsend();
18533262Ssklower private VOID	intwait();
18633262Ssklower private VOID	salarm();
18733262Ssklower private VOID	walarm();
18833262Ssklower private VOID	falarm();
18933262Ssklower private VOID	reverseready();
19033262Ssklower private VOID	readynow();
19133262Ssklower private VOID	emtdead();
19233262Ssklower private VOID	emtdone();
19333262Ssklower private char 	*FindPattern();
19433262Ssklower 
19533262Ssklower #define SENDALARM 90
19633262Ssklower #define WAITALARM 30
19733262Ssklower 
main(argc,argv)19834945Sedward main(argc, argv)
19933262Ssklower 	int argc;
20034945Sedward 	char **argv;
20133262Ssklower {
20233262Ssklower     register int cnt, wc;
20334945Sedward     char *cp;
20433262Ssklower     register char *mbp;
20533262Ssklower     long clock;		/* for log timestamp */
20633262Ssklower     char magic[11];	/* first few bytes of stdin ?magic number and type */
20733262Ssklower     int  reversing = 0;
20833262Ssklower     FILE *streamin;
20933262Ssklower     char mybuf[BUFSIZ];
21033262Ssklower     int wpid;
21133262Ssklower     union wait status;
21233262Ssklower     int fdpipe[2];
21333262Ssklower     int format = 0;
21433262Ssklower 
21533262Ssklower     VOIDC signal(SIGINT, intinit);
21633262Ssklower     VOIDC signal(SIGHUP, intinit);
21733262Ssklower     VOIDC signal(SIGQUIT, intinit);
21833262Ssklower     VOIDC signal(SIGTERM, intinit);
21933262Ssklower 
22033262Ssklower     /* parse command-line arguments */
22133262Ssklower     /* the argv (see header comments) comes from the spooler daemon */
22233262Ssklower     /* itself, so it should be canonical, but at least one 4.2-based */
22333262Ssklower     /* system uses -nlogin -hhost (insead of -n login -h host) so I */
22433262Ssklower     /* check for both */
22534945Sedward     BannerFirst = (cp = envget("BANNERFIRST")) != NULL ? atoi(cp) : 0;
22634945Sedward     BannerLast = (cp = envget("BANNERLAST")) != NULL ? atoi(cp) : 0;
22734945Sedward     VerboseLog = (cp = envget("VERBOSELOG")) != NULL ? atoi(cp) : 1;
22834945Sedward     reverse = envget("REVERSE");	/* name of the filter itself */
22934945Sedward     prog = *argv;
23034945Sedward     while (*++argv)
23134945Sedward 	if (**argv == '-') {
23234945Sedward 	    switch (argv[0][1]) {
23333262Ssklower 		case 'P':	/* printer name */
23433262Ssklower 		    argc--;
23534945Sedward 		    pname = *++argv;
23633262Ssklower 		    break;
23733262Ssklower 
23833262Ssklower 		case 'n': 	/* user name */
23933262Ssklower 		    argc--;
24034945Sedward 		    name = *++argv;
24133262Ssklower 		    break;
24233262Ssklower 
24333262Ssklower 		case 'h': 	/* host */
24433262Ssklower 		    argc--;
24534945Sedward 		    host = *++argv;
24633262Ssklower 		    break;
24733262Ssklower 
24833262Ssklower 		case 'p':	/* prog */
24933262Ssklower 		    argc--;
25034945Sedward 		    prog = *++argv;
25133262Ssklower 		    break;
25233262Ssklower 
25333262Ssklower 		case 'r':	/* never reverse */
25433262Ssklower 		    argc--;
25534945Sedward 		    reverse = NULL;
25633262Ssklower 		    break;
25733262Ssklower 
25833262Ssklower 		default:	/* unknown */
25934945Sedward 		    fprintf(stderr, "%s: unknown option: %s\n", prog, *argv);
26033262Ssklower 		    break;
26133262Ssklower 	    }
26234945Sedward 	} else
26334945Sedward 	    accountingfile = *argv;
26434945Sedward     debugp((stderr, "args: %s %s %s %s\n", prog, host, name, accountingfile));
26533262Ssklower 
26633262Ssklower     /* do printer-specific options processing */
26733262Ssklower 
26833262Ssklower     if (VerboseLog) {
26933262Ssklower 	fprintf(stderr, "%s: %s:%s %s start - %s", prog, host, name, pname,
27033262Ssklower             (VOIDC time(&clock), ctime(&clock)));
27133262Ssklower 	VOIDC fflush(stderr);
27233262Ssklower     }
27334945Sedward     debugp((stderr, "%s: pid %d ppid %d\n", prog, getpid(), getppid()));
27434945Sedward     debugp((stderr, "%s: options BF %d BL %d VL %d R %s\n", prog, BannerFirst,
27534945Sedward     	BannerLast, VerboseLog, reverse == NULL ? "norev" : reverse));
27633262Ssklower 
27733262Ssklower     /* IMPORTANT: in the case of cascaded filters, */
27833262Ssklower     /* stdin may be a pipe! (and hence we cannot seek!) */
27933262Ssklower 
28034945Sedward     switch (cnt = read(fileno(stdin), magic, 11)) {
28134945Sedward     case 11:
28234945Sedward 	debugp((stderr, "%s: magic number is %11.11s\n", prog, magic));
28334945Sedward 	streamin = stdin;
28434945Sedward 	if (strncmp(magic, "%!PS-Adobe-", 11) == 0)
28534945Sedward 	    goto go_ahead;
28634945Sedward     default:
28734945Sedward 	if (strncmp(magic, "%!", 2) == 0) {
28834945Sedward 	    reverse = NULL;
28934945Sedward 	    goto go_ahead;
29034945Sedward 	}
29134945Sedward 	break;
29234945Sedward     case 0:
29334945Sedward 	debugp((stderr, "%s: EOF reading magic number\n", prog));
29434945Sedward         fprintf(stderr,"%s: empty file\n", prog);
29534945Sedward 	goto badfile;
29634945Sedward     case -1:
29734945Sedward 	debugp((stderr, "%s: error reading magic number\n", prog));
29834945Sedward         fprintf(stderr,"%s: error reading magic number\n", prog);
29934945Sedward     badfile:
30034945Sedward 	VOIDC fflush(stderr);
30134945Sedward 	exit(THROW_AWAY);
30233262Ssklower     }
30334945Sedward     /*
30434945Sedward      * here is where you might test for other file type
30533262Ssklower      * e.g., PRESS, imPRESS, DVI, Mac-generated, etc.
30633262Ssklower      */
30734945Sedward     /*
30834945Sedward      * final sanity check on the text file, to guard
30933262Ssklower      * against arbitrary binary data
31033262Ssklower      */
31134945Sedward     while (--cnt >= 0)
31234945Sedward 	if (!isascii(magic[cnt]) ||
31334945Sedward 	    !isprint(magic[cnt]) && !isspace(magic[cnt])) {
31434945Sedward 	    fprintf(stderr, "%s: spooled binary file rejected\n", prog);
31533262Ssklower 	    VOIDC fflush(stderr);
31634945Sedward 	    sprintf(mybuf, "%s/bogusmsg.ps", envget("PSLIBDIR"));
31734945Sedward 	    if ((streamin = freopen(mybuf, "r", stdin)) == NULL)
31833262Ssklower 		exit(THROW_AWAY);
31933262Ssklower 	    format = 1;
32033262Ssklower 	    goto lastchance;
32133262Ssklower 	}
32234945Sedward     /* exec dumb formatter to make a listing */
32334945Sedward     debugp((stderr, "formatting\n"));
32434945Sedward     format = 1;
32534945Sedward     VOIDC lseek(0, 0L, 0);
32634945Sedward     if (pipe(fdpipe) < 0)
32734945Sedward 	pexit2(prog, "format pipe", THROW_AWAY);
32834945Sedward     if ((fpid = fork()) < 0)
32934945Sedward 	pexit2(prog, "format fork", THROW_AWAY);
33034945Sedward     if (fpid == 0) {		/* child */
33134945Sedward 	/* set up child stdout to feed parent stdin */
33234945Sedward 	if (close(1) < 0 || dup(fdpipe[1]) != 1 ||
33334945Sedward 	    close(fdpipe[1]) < 0 || close(fdpipe[0]) < 0)
33434945Sedward 	    pexit2(prog, "format child", THROW_AWAY);
33534945Sedward 	execl(envget("PSTEXT"), "pstext", pname, 0);
33634945Sedward 	pexit2(prog, "format exec", THROW_AWAY);
33733262Ssklower     }
33834945Sedward     /* parent continues, set up stdin to be pipe */
33934945Sedward     if (close(0) < 0 || dup(fdpipe[0]) != 0 ||
34034945Sedward 	close(fdpipe[0]) < 0 || close(fdpipe[1]) < 0)
34134945Sedward 	pexit2(prog, "format parent", THROW_AWAY);
34234945Sedward     /* fall through to spooler with new stdin */
34334945Sedward     /* can't seek here but we should be at the right place */
34434945Sedward     streamin = fdopen(0, "r");
34533262Ssklower 
34634945Sedward go_ahead:
34733262Ssklower     /* do page reversal if specified */
34834945Sedward     if (reversing = reverse != NULL) {
34934945Sedward 	debugp((stderr, "reversing\n"));
35033262Ssklower 	VOIDC setjmp(waitonreverse);
35133262Ssklower 	if (!revdone) {
35233262Ssklower 	    VOIDC signal(SIGEMT, reverseready);
35334945Sedward 	    if (pipe(fdpipe) < 0)
35434945Sedward 		pexit2(prog, "reverse pipe", THROW_AWAY);
35534945Sedward 	    if ((fpid = fork()) < 0)
35634945Sedward 		pexit2(prog, "reverse fork", THROW_AWAY);
35734945Sedward 	    if (fpid == 0) {		/* child */
35833262Ssklower 		/* set up child stdout to feed parent stdin */
35934945Sedward 		if (close(1) < 0 || dup(fdpipe[1]) != 1 ||
36034945Sedward 		    close(fdpipe[1]) < 0 || close(fdpipe[0]) < 0)
36133262Ssklower 		    pexit2(prog, "reverse child", THROW_AWAY);
36233262Ssklower 		execl(reverse, "psrv", pname, 0);
36334945Sedward 		pexit2(prog, "reverse exec", THROW_AWAY);
36433262Ssklower 	    }
36533262Ssklower 	    /* parent continues */
36634945Sedward 	    if (close(0) < 0 || dup(fdpipe[0]) != 0 ||
36734945Sedward 		close(fdpipe[0]) < 0 || close(fdpipe[1]) < 0)
36833262Ssklower 		pexit2(prog, "reverse parent", THROW_AWAY);
36933262Ssklower 	    /* fall through to spooler with new stdin */
37034945Sedward 	    streamin = fdopen(0, "r");
37134945Sedward 	    while (!revdone)
37233262Ssklower 		pause();
37333262Ssklower 	}
37433262Ssklower 	VOIDC signal(SIGEMT, SIG_IGN);
37534945Sedward 	debugp((stderr, "%s: reverse feeding\n", prog));
37633262Ssklower     }
37733262Ssklower 
37834945Sedward lastchance:
37934945Sedward     /*
38034945Sedward      * establish an input stream from the printer --
38133262Ssklower      * the printcap entry specifies "rw" and we get
38233262Ssklower      * invoked with stdout == the device, so we
38333262Ssklower      * dup stdout, and reopen it for reading;
38433262Ssklower      * this seems to work fine...
38533262Ssklower      */
38634945Sedward     fdinput = fileno(streamin);		/* the file to print */
38734945Sedward     fdsend = fileno(stdout);		/* the printer (write) */
38834945Sedward     if ((fdlisten = dup(fdsend)) < 0)	/* the printer (read) */
38934945Sedward 	pexit(prog, THROW_AWAY);
39034945Sedward     doactng = name && accountingfile && access(accountingfile, W_OK) == 0;
39134945Sedward     /*
39234945Sedward      * get control of the "status" message file.
39333262Ssklower      * we copy the current one to ".status" so we can restore it
39433262Ssklower      * on exit (to be clean).
39533262Ssklower      * Our ability to use this is publicized nowhere in the
39633262Ssklower      * 4.2 lpr documentation, so things might go bad for us.
39733262Ssklower      * We will use it to report that printer errors condition
39833262Ssklower      * has been detected, and the printer should be checked.
39933262Ssklower      * Unfortunately, this notice may persist through
40033262Ssklower      * the end of the print job, but this is no big deal.
40133262Ssklower      */
40234945Sedward     BackupStatus(".status", "status");
40334945Sedward     if ((cpid = fork()) < 0)
40434945Sedward 	pexit(prog, THROW_AWAY);
40534945Sedward     if (cpid) {			/* parent - sender */
40633262Ssklower 	VOIDC setjmp(sendint);
40733262Ssklower 	if (intrup) {
40833262Ssklower 	    /* we only get here if there was an interrupt */
40933262Ssklower 
41033262Ssklower 	    fprintf(stderr,"%s: abort (sending)\n",prog);
41133262Ssklower 	    VOIDC fflush(stderr);
41233262Ssklower 
41333262Ssklower 	    /* flush and restart output to printer,
41433262Ssklower 	     * send an abort (^C) request and wait for the job to end
41533262Ssklower 	     */
41633262Ssklower 	    if (ioctl(fdsend, TIOCFLUSH,&flg) || ioctl(fdsend, TIOCSTART,&flg)
41734945Sedward 	        || (write(fdsend, abortbuf, 1) != 1)) {
41833262Ssklower 		RestoreStatus();
41933262Ssklower 		pexit(prog,THROW_AWAY);
42033262Ssklower 	    }
42133262Ssklower 	    debugp((stderr,"%s: sent interrupt - waiting\n",prog));
42233262Ssklower 	    intrup = 0;
42333262Ssklower 	    goto donefile; /* sorry ewd! */
42433262Ssklower 	}
42533262Ssklower 
42633262Ssklower         VOIDC signal(SIGINT, intsend);
42733262Ssklower         VOIDC signal(SIGHUP, intsend);
42833262Ssklower         VOIDC signal(SIGQUIT, intsend);
42933262Ssklower         VOIDC signal(SIGTERM, intsend);
43033262Ssklower 	VOIDC signal(SIGEMT, readynow);
43133262Ssklower 
43233262Ssklower 	progress = oldprogress = 0; /* finite progress on sender */
43333262Ssklower 	getstatus = FALSE; /* prime the pump for fun FALSE; */
43433262Ssklower 
43533262Ssklower 	VOIDC signal(SIGALRM, salarm); /* sending phase alarm */
43633262Ssklower 	VOIDC alarm(SENDALARM); /* schedule an alarm/timeout */
43733262Ssklower 
43833262Ssklower 	/* loop, trying to send a ^T to get printer status
43933262Ssklower 	 * We will hang here (and post a message) if the printer
44033262Ssklower 	 * is unreachable.  Eventually, we will succeed, the listener
44133262Ssklower 	 * will see the status report, signal us, and we will proceed
44233262Ssklower 	 */
44333262Ssklower 
44433262Ssklower 	cnt = 1;
44533262Ssklower 	VOIDC setjmp(startstatus);
44633262Ssklower 
44734945Sedward 	while (!goahead) {
44834945Sedward 	    debugp((stderr, "%s: get start status\n", prog));
44933262Ssklower 	    VOIDC write(fdsend, statusbuf, 1);
45033262Ssklower 	    pause();
45134945Sedward 	    if (goahead)
45234945Sedward 		break;
45333262Ssklower 	    /* if we get here, we got an alarm */
45433262Ssklower 	    ioctl(fdsend, TIOCFLUSH, &flg);
45533262Ssklower 	    ioctl(fdsend, TIOCSTART, &flg);
45634945Sedward 	    sprintf(mybuf, "not responding for %d minutes",
45733262Ssklower 	    	(cnt * SENDALARM+30)/60);
45833262Ssklower 	    Status(mybuf);
45933262Ssklower 	    alarm(SENDALARM);
46033262Ssklower 	    cnt++;
46133262Ssklower 	}
46233262Ssklower 
46333262Ssklower 	VOIDC signal(SIGEMT, emtdead); /* now EMTs mean printer died */
46433262Ssklower 
46533262Ssklower 	RestoreStatus();
46633262Ssklower 	debugp((stderr,"%s: printer responding\n",prog));
46733262Ssklower 
46833262Ssklower 	/* initial page accounting (BEFORE break page) */
46933262Ssklower 	if (doactng) {
47034945Sedward 	    sprintf(mybuf, getpages, eofbuf);
47133262Ssklower 	    VOIDC write(fdsend, mybuf, strlen(mybuf));
47233263Ssklower 	    debugp((stderr, "%s: sent pagecount request\n", prog));
47333262Ssklower 	    progress++;
47433262Ssklower 
47533263Ssklower             /* Sat Oct 31 17:51:45 PST 1987
47633263Ssklower              * loop, waiting for the listener to signal initial pagecount is
47733263Ssklower              * received.  The problem is with fast machines and short jobs;
47833263Ssklower              * if we don't loop here, we may finish the job and send another
47933263Ssklower              * CTRL-T before the initial pagecount ever came back.  The way
48033263Ssklower              * the laserwriter behaves, this may result in a mix of pagecount
48133263Ssklower              * data and status information like this:
48233263Ssklower              * %%[ pagecount: %%[ status: busy; source: serial 25 ]%% 24418 ]%%
48333263Ssklower              *
48433263Ssklower              * That is really silly - Cuong
48533263Ssklower              */
48633263Ssklower 
48733263Ssklower 	    VOIDC signal(SIGINT, intsend);
48833263Ssklower 	    VOIDC signal(SIGHUP, intsend);
48933263Ssklower 	    VOIDC signal(SIGQUIT, intsend);
49033263Ssklower 	    VOIDC signal(SIGTERM, intsend);
49133263Ssklower 	    VOIDC signal(SIGEMT, readynow);
49233263Ssklower 
49333263Ssklower 	    progress = oldprogress = 0; /* finite progress on sender */
49433263Ssklower 	    getstatus = FALSE; /* prime the pump for fun FALSE; */
49533263Ssklower 
49633263Ssklower 	    VOIDC signal(SIGALRM, salarm); /* sending phase alarm */
49733263Ssklower 	    VOIDC alarm(SENDALARM); /* schedule an alarm/timeout */
49833263Ssklower 
49933263Ssklower 	    cnt = 1; goahead = FALSE;
50033263Ssklower 	    VOIDC setjmp(startstatus);
50133263Ssklower 
50234945Sedward 	    while (!goahead) {
50333263Ssklower 		pause();
50434945Sedward 		if (goahead)
50534945Sedward 		    break;
50633263Ssklower 		/* if we get here, we got an alarm */
50733263Ssklower 		ioctl(fdsend, TIOCFLUSH, &flg);
50833263Ssklower 		ioctl(fdsend, TIOCSTART, &flg);
50934945Sedward 		sprintf(mybuf, "not responding for %d minutes",
51033263Ssklower 		    (cnt * SENDALARM+30)/60);
51133263Ssklower 		Status(mybuf);
51233263Ssklower 		alarm(SENDALARM);
51333263Ssklower 		cnt++;
51433263Ssklower 	    }
51533263Ssklower 
51633263Ssklower 	    VOIDC signal(SIGEMT, emtdead); /* now EMTs mean printer died */
51733263Ssklower 
51833263Ssklower 	    RestoreStatus();
51933263Ssklower 	    debugp((stderr,"%s: sender received EMT (goahead) from listener\n",prog));
52033263Ssklower         } /* if (doactng) */
52133263Ssklower 
52233262Ssklower 	/* initial break page ? */
52333262Ssklower 	if (BannerFirst) {
52433262Ssklower 	    SendBanner();
52533262Ssklower 	    progress++;
52633262Ssklower 	}
52733262Ssklower 
52833262Ssklower 	/* ship the magic number! */
52934945Sedward 	if (!format && !reversing) {
53034945Sedward 	   VOIDC write(fdsend, magic, 11);
53133262Ssklower 	   progress++;
53233262Ssklower 	}
53333262Ssklower 
53433262Ssklower 	/* now ship the rest of the file */
53533262Ssklower 
53633262Ssklower 	VOIDC alarm(SENDALARM); /* schedule an alarm */
53733262Ssklower 
53833262Ssklower 	while ((cnt = read(fdinput, mybuf, sizeof mybuf)) > 0) {
53933262Ssklower 	    /* VOIDC alarm(SENDALARM);	/* we made progress, reset alarm */
54033262Ssklower 	    if (intrup == TRUE) break;
54133262Ssklower 
54233262Ssklower 	    /* get status every other time */
54333262Ssklower 	    if (getstatus) {
54433263Ssklower 		debugp((stderr,"%s: get periodic status\n",prog));
54533262Ssklower 		VOIDC write(fdsend, statusbuf, 1);
54633262Ssklower 		getstatus = FALSE;
54733262Ssklower 		progress++;
54833262Ssklower 	    }
54933262Ssklower 	    mbp = mybuf;
55033262Ssklower 	    while ((cnt > 0) && ((wc = write(fdsend, mbp, cnt)) != cnt)) {
55133262Ssklower 		/* this seems necessary but not sure why */
55233262Ssklower 		if (wc < 0) {
55333262Ssklower 		    fprintf(stderr,"%s: error writing to printer:\n",prog);
55433262Ssklower 		    perror(prog);
55533262Ssklower 		    RestoreStatus();
55633262Ssklower 		    sleep(10);
55733262Ssklower 		    exit(TRY_AGAIN);
55833262Ssklower 		}
55933262Ssklower 		mbp += wc;
56033262Ssklower 		cnt -= wc;
56133262Ssklower 		progress++;
56233262Ssklower 	    }
56333262Ssklower 	    progress++;
56433262Ssklower 	}
56533262Ssklower 	if (cnt < 0) {
56633262Ssklower 	    fprintf(stderr,"%s: error reading from stdin: \n", prog);
56733262Ssklower 	    perror(prog);
56833262Ssklower 	    RestoreStatus();
56933262Ssklower 	    sleep(10);
57033262Ssklower 	    exit(TRY_AGAIN);	/* kill the listener? */
57133262Ssklower 	}
57233262Ssklower 
57333263Ssklower 	/* final break page ? */
57433263Ssklower 	if (BannerLast) {
57533263Ssklower 	    SendBanner();
57633263Ssklower 	    progress++;
57733263Ssklower 	}
57833262Ssklower 
57933262Ssklower 	donefile:;
58033262Ssklower 
58133262Ssklower 	sendend = 1;
58233262Ssklower 
58333262Ssklower 	VOIDC setjmp(dwait);
58433262Ssklower 
58533262Ssklower 	if (sendend && !gotemt) {
58633262Ssklower 
58733262Ssklower 	    VOIDC signal(SIGEMT, emtdone);
58833262Ssklower 
58933262Ssklower 	    debugp((stderr,"%s: done sending\n",prog));
59033262Ssklower 
59133262Ssklower 	    /* now send the PostScript EOF character */
59233263Ssklower 	    debugp((stderr,"%s: sending PostScript EOF\n",prog));
59333262Ssklower 	    VOIDC write(fdsend, eofbuf, 1);
59433262Ssklower 	    sendend = 0;
59533262Ssklower 	    progress++;
59633262Ssklower 
59733262Ssklower 	    VOIDC signal(SIGINT, intwait);
59833262Ssklower 	    VOIDC signal(SIGHUP, intwait);
59933262Ssklower 	    VOIDC signal(SIGQUIT, intwait);
60033262Ssklower 	    VOIDC signal(SIGTERM, intwait);
60133262Ssklower 
60233262Ssklower 	    VOIDC signal(SIGALRM, walarm);
60333262Ssklower 	    VOIDC alarm(WAITALARM);
60433262Ssklower 	    getstatus = TRUE;
60533262Ssklower 	}
60633262Ssklower 
60733263Ssklower 	/* for very short jobs and very fast machines,
60833263Ssklower 	 * we've experienced that the whole job is sent
60933263Ssklower 	 * before the LaserWriter has a chance to update
61033263Ssklower 	 * its status.  Hence we may get a false idle
61133263Ssklower 	 * status if we immediately send the statusbuf.
61233263Ssklower 	 *
61333263Ssklower 	 * Keep in mind that the LaserWriter status response
61433263Ssklower 	 * is asynchronous to the datastream.
61533263Ssklower 	 */
61633263Ssklower 	sleep(1);
61733263Ssklower 
61833262Ssklower 	/* wait to sync with listener EMT signal
61933262Ssklower 	 * to indicate it got an EOF from the printer
62033262Ssklower 	 */
62134945Sedward 	for (;;) {
62233262Ssklower 	    if (gotemt) break;
62333262Ssklower 	    if (getstatus) {
62433263Ssklower 		debugp((stderr,"%s: get final status\n",prog));
62533262Ssklower 		VOIDC write(fdsend, statusbuf, 1);
62633262Ssklower 		getstatus = FALSE;
62733262Ssklower 	    }
62833262Ssklower 	    debugp((stderr,"waiting e%d i%d %d %d\n",
62933262Ssklower 	    	gotemt,intrup,wpid,status));
63033262Ssklower 	    wpid = wait(&status);
63133262Ssklower 	    if (wpid == -1) break;
63233262Ssklower 	}
63333262Ssklower 
63433262Ssklower 	/* final page accounting */
63533262Ssklower 	if (doactng) {
63634945Sedward 	    sprintf(mybuf, getpages, eofbuf);
63733262Ssklower 	    VOIDC write(fdsend, mybuf, strlen(mybuf));
63833263Ssklower 	    debugp((stderr, "%s: sent pagecount request\n", prog));
63933262Ssklower 	    progress++;
64033262Ssklower         }
64133262Ssklower 
64233262Ssklower 	/* wait for listener to die */
64333262Ssklower 	VOIDC setjmp(dwait);
64434945Sedward         while ((wpid = wait(&status)) > 0)
64534945Sedward 	    ;
64633262Ssklower 	VOIDC alarm(0);
64733262Ssklower 	VOIDC signal(SIGINT, SIG_IGN);
64833262Ssklower 	VOIDC signal(SIGHUP, SIG_IGN);
64933262Ssklower 	VOIDC signal(SIGQUIT, SIG_IGN);
65033262Ssklower 	VOIDC signal(SIGTERM, SIG_IGN);
65133262Ssklower 	VOIDC signal(SIGEMT, SIG_IGN);
65233262Ssklower 	debugp((stderr,"w2: s%lo p%d = p%d\n", status, wpid, cpid));
65333262Ssklower 
65433262Ssklower 	if (VerboseLog) {
65534945Sedward 	    fprintf(stderr, "%s: end - %s", prog,
65634945Sedward 		(time(&clock), ctime(&clock)));
65733262Ssklower 	    VOIDC fflush(stderr);
65833262Ssklower 	}
65934945Sedward 	RestoreStatus();
66034945Sedward 	exit(0);
66134945Sedward     } else {			/* child - listener */
66233262Ssklower       register FILE *psin;
66333262Ssklower       register int r;
66433262Ssklower       char pbuf[BUFSIZ]; /* buffer for pagecount info */
66533262Ssklower       char *pb;		/* pointer for above */
66633262Ssklower       int pc1, pc2; 	/* page counts before and after job */
66733262Ssklower       int sc;		/* pattern match count for sscanf */
66833262Ssklower       int ppid;		/* parent process id */
66933262Ssklower 
67033262Ssklower       VOIDC signal(SIGINT, SIG_IGN);
67133262Ssklower       VOIDC signal(SIGHUP, SIG_IGN);
67233262Ssklower       VOIDC signal(SIGQUIT, SIG_IGN);
67333262Ssklower       VOIDC signal(SIGTERM, SIG_IGN);
67433262Ssklower       VOIDC signal(SIGALRM, SIG_IGN);
67533262Ssklower 
67633262Ssklower       ppid = getppid();
67733262Ssklower 
67833262Ssklower       /* get jobout from environment if there, otherwise use stderr */
67934945Sedward       if ((cp = envget("JOBOUTPUT")) == NULL ||
68034945Sedward 	  (jobout = fopen(cp, "w")) == NULL)
68133262Ssklower 	  jobout = stderr;
68233262Ssklower 
68333262Ssklower       pc1 = pc2 = -1; /* bogus initial values */
68433262Ssklower       if ((psin = fdopen(fdlisten, "r")) == NULL) {
68533262Ssklower 	  RestoreStatus();
68633262Ssklower 	  pexit(prog, THROW_AWAY);
68733262Ssklower       }
68833262Ssklower 
68933262Ssklower       /* listen for first status (idle?) */
69034945Sedward       for (pb = pbuf;;) {
69133262Ssklower 	  r = getc(psin);
69233262Ssklower 	  if (r == EOF) {
69333262Ssklower 	      fprintf(stderr, EOFerr, prog, "startup");
69433262Ssklower 	      VOIDC fflush(stderr);
69533262Ssklower 	      sleep(20); /* printer may be coming up */
69634945Sedward 	      continue;
69733262Ssklower 	      /* RestoreStatus(); */
69833262Ssklower 	      /* exit(TRY_AGAIN); */
69933262Ssklower 	  }
70034945Sedward 	  if ((r & 0377) == '\n')
70134945Sedward 	      break;
70233262Ssklower 	  *pb++ = r;
70333262Ssklower       }
70433262Ssklower       *pb = 0;
70534945Sedward       debugp((stderr, "%s: initial status - %s\n", prog, pbuf));
70633262Ssklower       if (strcmp(pbuf, "%%[ status: idle ]%%\r") != 0) {
70734945Sedward 	  fprintf(stderr,"%s: initial status - %s\n", prog, pbuf);
70833262Ssklower 	  VOIDC fflush(stderr);
70933262Ssklower       }
71033262Ssklower 
71133262Ssklower       /* flush input state and signal sender that we heard something */
71233262Ssklower       ioctl(fdlisten, TIOCFLUSH, &flg);
71333262Ssklower 
71433262Ssklower       VOIDC kill(ppid,SIGEMT);
71533262Ssklower 
71633262Ssklower       /* listen for first pagecount */
71733262Ssklower       if (doactng) {
71833262Ssklower         pb = pbuf;
71933262Ssklower 	*pb = '\0';
72033262Ssklower 	while (TRUE) {
72133262Ssklower 	  r = getc(psin);
72233262Ssklower 	  if (r == EOF) {
72333262Ssklower 	      fprintf(stderr, EOFerr, prog, "accounting1");
72433262Ssklower 	      VOIDC fflush(stderr);
72533262Ssklower 	      RestoreStatus();
72633262Ssklower 	      sleep(10);	/* give interface a chance */
72733262Ssklower 	      exit(TRY_AGAIN);
72833262Ssklower 	  }
72934945Sedward 	  if ((r&0377) == PS_EOF)
73034945Sedward 		break;
73133262Ssklower 	  *pb++ = r;
73233262Ssklower 	}
73333262Ssklower 	*pb = '\0';
73433262Ssklower 
73533262Ssklower 	if (pb = FindPattern(pb, pbuf, "%%[ pagecount: ")) {
73633262Ssklower 	    sc = sscanf(pb, "%%%%[ pagecount: %d ]%%%%\r", &pc1);
73733262Ssklower 	}
73833262Ssklower 	if ((pb == NULL) || (sc != 1)) {
73933262Ssklower 	    fprintf(stderr, "%s: accounting error 1 (%s)\n", prog,pbuf);
74033262Ssklower 	    VOIDC fflush(stderr);
74133262Ssklower 	}
74233262Ssklower 	debugp((stderr,"%s: accounting 1 (%s)\n",prog,pbuf));
74333263Ssklower 
74433263Ssklower         /* flush input state and signal sender that we heard something */
74533263Ssklower         ioctl(fdlisten, TIOCFLUSH, &flg);
74633263Ssklower 
74733263Ssklower         VOIDC kill(ppid,SIGEMT);
74833263Ssklower 
74933263Ssklower 	/*
75033263Ssklower 	    Sun Sep 20 18:32:28 PDT 1987
75133263Ssklower 	    The previous bug was that it was assumed the ctrl-d comes
75233263Ssklower 	    before the final pagecount.  This doesn't happen, and the
75333263Ssklower 	    listener waits forever after a ctrl-d for a pagecount.
75433263Ssklower 	    The fix is to clear out the pbuf[] buffer, then check for it
75533263Ssklower 	    when we get to looking for the final pagecount.  If it is
75633263Ssklower 	    non-empty, we know we *already* read the final pagecount
75733263Ssklower 	    *before* the ctrl-d, and use it, without waiting for
75833263Ssklower 	    anything to come back from the printer.
75933263Ssklower 	*/
76033263Ssklower 	pbuf[0] = '\0';
76133262Ssklower       }
76233262Ssklower 
76333262Ssklower       /* listen for the user job */
76433262Ssklower       while (TRUE) {
76533262Ssklower 	r = getc(psin);
76633263Ssklower 	debugp((stderr, "%s: listener got character \\%o '%c'\n", prog, r, r));
76734945Sedward 	if ((r&0377) == PS_EOF)
76834945Sedward 		break;
76934945Sedward 	if (r == EOF) {
77033262Ssklower 	    VOIDC fclose(psin);
77133262Ssklower 	    fprintf(stderr, EOFerr, prog, "job");
77233262Ssklower 	    VOIDC fflush(stderr);
77333262Ssklower 	    RestoreStatus();
77433262Ssklower 	    VOIDC kill(ppid,SIGEMT);
77533262Ssklower 	    exit(THROW_AWAY);
77633262Ssklower 	}
77733263Ssklower /*
77833263Ssklower     Sun Sep 20 18:37:01 PDT 1987
77933263Ssklower     GotChar() takes an addition argument: the pointer to the
78033263Ssklower     pbuf[] buffer, and fills it with the final pagecount
78133263Ssklower     information if that is received from the printer.
78233263Ssklower */
78333263Ssklower 	GotChar(r, pbuf);
78433262Ssklower       }
78533262Ssklower 
78633262Ssklower       /* let sender know we saw the end of the job */
78733262Ssklower       /* sync - wait for sender to restart us */
78833262Ssklower 
78933262Ssklower       debugp((stderr,"%s: listener saw eof, signaling\n",prog));
79033262Ssklower 
79133262Ssklower       VOIDC kill(ppid,SIGEMT);
79233262Ssklower 
79333262Ssklower       /* now get final page count */
79433262Ssklower       if (doactng) {
79533263Ssklower /*
79633263Ssklower     Sun Sep 20 18:48:35 PDT 1987
79733263Ssklower     We attempt to wait for the final pagecount only if it has *not*
79833263Ssklower     been sent by the printer.  It is the case that the final pagecount
79933263Ssklower     is sent before the ctrl-d above, hence if we wait, it'll be forever.
80033263Ssklower     Final pagecount information 'prematurely' received has already
80133263Ssklower     been stored in pbuf[] iff pbuf[0] is non-null.
80233263Ssklower */
80333263Ssklower 
80433263Ssklower 	if (pbuf[0] == '\0') {
80533263Ssklower 	    debugp((stderr, "%s: waiting for pagecount\n", prog));
80633263Ssklower 	    pb = pbuf;
80733263Ssklower 	    *pb = '\0';	/* ignore the previous pagecount */
80833263Ssklower 	    while (TRUE) {
80933263Ssklower 	      r = getc(psin);
81033263Ssklower 	      if (r == EOF) {
81133263Ssklower 		  fprintf(stderr, EOFerr, prog, "accounting2");
81233263Ssklower 		  VOIDC fflush(stderr);
81333263Ssklower 		  RestoreStatus();
81433263Ssklower 		  sleep(10);
81533263Ssklower 		  exit(THROW_AWAY); /* what else to do? */
81633263Ssklower 	      }
81734945Sedward 	      if ((r&0377) == PS_EOF)
81834945Sedward 		break;
81933263Ssklower 	      *pb++ = r;
82033263Ssklower 	    }
82133263Ssklower 	    *pb = '\0';
82233263Ssklower 	} else {
82333263Ssklower 	    pb = pbuf + strlen(pbuf) - 1;
82433262Ssklower 	}
82533262Ssklower 	debugp((stderr,"%s: accounting 2 (%s)\n",prog,pbuf));
82633262Ssklower 	if (pb = FindPattern(pb, pbuf, "%%[ pagecount: ")) {
82733262Ssklower 	    sc = sscanf(pb, "%%%%[ pagecount: %d ]%%%%\r", &pc2);
82833262Ssklower 	}
82933262Ssklower 	if ((pb == NULL) || (sc != 1)) {
83033262Ssklower 	    fprintf(stderr, "%s: accounting error 2 (%s)\n", prog,pbuf);
83133262Ssklower 	    VOIDC fflush(stderr);
83233262Ssklower 	}
83333262Ssklower         else if ((pc2 < pc1) || (pc1 < 0) || (pc2 < 0)) {
83433262Ssklower 	    fprintf(stderr,"%s: accounting error 3 %d %d\n", prog,pc1,pc2);
83533262Ssklower 	    VOIDC fflush(stderr);
83633262Ssklower 	}
83733262Ssklower 	else if (freopen(accountingfile, "a", stdout) != NULL) {
83833262Ssklower 	  printf("%7.2f\t%s:%s\n", (float)(pc2 - pc1), host, name);
83933262Ssklower 	  VOIDC fclose(stdout);
84033263Ssklower /*
84133263Ssklower     Sun Sep 20 18:55:32 PDT 1987
84233263Ssklower     File append failure report added for future use.
84333263Ssklower */
84433263Ssklower 	} else {
84533263Ssklower 	  debugp((stderr, "%s: can't append accounting file\n", prog));
84633263Ssklower 	  perror(accountingfile);
84733262Ssklower 	}
84833262Ssklower       }
84933262Ssklower 
85033262Ssklower       /* all done -- let sender know */
85134945Sedward       /* no need to close files */
85234945Sedward       exit(0);
85333262Ssklower     }
85433262Ssklower }
85533262Ssklower 
85633262Ssklower /* send the file ".banner" */
SendBanner()85733262Ssklower private SendBanner()
85833262Ssklower {
85933262Ssklower     register int banner;
86033262Ssklower     int cnt;
86133262Ssklower     char buf[BUFSIZ];
86233262Ssklower 
86334945Sedward     if ((banner = open(".banner", O_RDONLY|O_NDELAY, 0)) < 0)
86434945Sedward 	return;
86534945Sedward     while ((cnt = read(banner, buf, sizeof buf)) > 0)
86634945Sedward 	VOIDC write(fdsend, buf, cnt);
86733262Ssklower     VOIDC close(banner);
86833796Skarels     VOIDC unlink(".banner");
86933262Ssklower }
87033262Ssklower 
87133262Ssklower /* search backwards from p in start for patt */
FindPattern(p,start,patt)87233262Ssklower private char *FindPattern(p, start, patt)
87333262Ssklower char *p;
87433262Ssklower char *start;
87533262Ssklower char *patt;
87633262Ssklower {
87733262Ssklower     int patlen;
87833262Ssklower     patlen = strlen(patt);
87933262Ssklower 
88033262Ssklower     p -= patlen;
88133262Ssklower     for (; p >= start; p--) {
88233262Ssklower 	if (strncmp(p, patt, patlen) == 0) return(p);
88333262Ssklower     }
88433262Ssklower     return ((char *)NULL);
88533262Ssklower }
88633262Ssklower 
GotChar(c,pbuf)88733263Ssklower private GotChar(c, pbuf)
88833262Ssklower register int c;
88933263Ssklower char	*pbuf;
89033262Ssklower {
89133262Ssklower     static char linebuf[BUFSIZ];
89233262Ssklower     static char *cp = linebuf;
89333262Ssklower     static enum State {normal, onep, twop, inmessage,
89433262Ssklower     			close1, close2, close3, close4} st = normal;
89533262Ssklower     char *match, *last;
89633262Ssklower 
89733262Ssklower     switch (st) {
89833262Ssklower 	case normal:
89933262Ssklower 	    if (c == '%') {
90033262Ssklower 		st = onep;
90133262Ssklower 		cp = linebuf;
90233262Ssklower 		*cp++ = c;
90333262Ssklower 		break;
90433262Ssklower 	    }
90533262Ssklower 	    putc(c,jobout);
90633262Ssklower 	    VOIDC fflush(jobout);
90733262Ssklower 	    break;
90833262Ssklower 	case onep:
90933262Ssklower 	    if (c == '%') {
91033262Ssklower 		st = twop;
91133262Ssklower 		*cp++ = c;
91233262Ssklower 		break;
91333262Ssklower 	    }
91433262Ssklower 	    putc('%',jobout);
91533262Ssklower 	    putc(c,jobout);
91633262Ssklower 	    VOIDC fflush(jobout);
91733262Ssklower 	    st = normal;
91833262Ssklower 	    break;
91933262Ssklower 	case twop:
92033262Ssklower 	    if (c == '\[') {
92133262Ssklower 		st = inmessage;
92233262Ssklower 		*cp++ = c;
92333262Ssklower 		break;
92433262Ssklower 	    }
925*64050Sbostic 	    if (c == '%') {
92633262Ssklower 		putc('%',jobout);
92733262Ssklower 		VOIDC fflush(jobout);
92833262Ssklower 		/* don't do anything to cp */
92933262Ssklower 		break;
93033262Ssklower 	    }
93133262Ssklower 	    putc('%',jobout);
93233262Ssklower 	    putc('%',jobout);
93333262Ssklower 	    VOIDC fflush(jobout);
93433262Ssklower 	    st = normal;
93533262Ssklower 	    break;
93633262Ssklower 	case inmessage:
93733262Ssklower 	    *cp++ = c;
938*64050Sbostic 	    if (c == ']') st = close1;
93933262Ssklower 	    break;
94033262Ssklower 	case close1:
94133262Ssklower 	    *cp++ = c;
94233262Ssklower 	    switch (c) {
94333262Ssklower 		case '%': st = close2; break;
944*64050Sbostic 		case ']': st = close1; break;
94533262Ssklower 		default: st = inmessage; break;
94633262Ssklower 	    }
94733262Ssklower 	    break;
94833262Ssklower 	case close2:
94933262Ssklower 	    *cp++ = c;
95033262Ssklower 	    switch (c) {
95133262Ssklower 		case '%': st = close3; break;
952*64050Sbostic 		case ']': st = close1; break;
95333262Ssklower 		default: st = inmessage; break;
95433262Ssklower 	    }
95533262Ssklower 	    break;
95633262Ssklower 	case close3:
95733262Ssklower 	    *cp++ = c;
95833262Ssklower 	    switch (c) {
95933262Ssklower 		case '\r': st = close4; break;
960*64050Sbostic 		case ']': st = close1; break;
96133262Ssklower 		default: st = inmessage; break;
96233262Ssklower 	    }
96333262Ssklower 	    break;
96433262Ssklower 	case close4:
96533262Ssklower 	    *cp++ = c;
96633262Ssklower 	    switch(c) {
96733262Ssklower 		case '\n': st = normal; break;
968*64050Sbostic 		case ']': st = close1; break;
96933262Ssklower 		default: st = inmessage; break;
97033262Ssklower 	    }
97133262Ssklower 	    if (st == normal) {
97233262Ssklower 		/* parse complete message */
97333262Ssklower 		last = cp;
97433262Ssklower 		*cp = 0;
97533262Ssklower 		debugp((stderr,">>%s",linebuf));
97633262Ssklower 		if (match = FindPattern(cp, linebuf, " PrinterError: ")) {
97733262Ssklower 		    if (*(match-1) != ':') {
97833262Ssklower 			fprintf(stderr,"%s",linebuf);
97933262Ssklower 			VOIDC fflush(stderr);
98033262Ssklower 			*(last-6) = 0;
98133262Ssklower 			Status(match+15);
98233262Ssklower 		    }
98333262Ssklower 		    else {
98433262Ssklower 			last = index(match,';');
98533262Ssklower 			*last = 0;
98633262Ssklower 			Status(match+15);
98733262Ssklower 		    }
98833262Ssklower 		}
98933262Ssklower 		else if (match = FindPattern(cp, linebuf, " status: ")) {
99033262Ssklower 		    match += 9;
99133262Ssklower 		    if (strncmp(match,"idle",4) == 0) {
99233262Ssklower 			/* we are hopelessly lost, get everyone to quit */
99333262Ssklower 			fprintf(stderr,"%s: ERROR: printer is idle, giving up!\n",prog);
99433262Ssklower 			VOIDC fflush(stderr);
99533262Ssklower 			VOIDC kill(getppid(),SIGKILL); /* will this work */
99633262Ssklower 			exit(THROW_AWAY);
99733262Ssklower 		    }
99833262Ssklower 		    else {
99933262Ssklower 			/* one of: busy, waiting, printing, initializing */
100033262Ssklower 			/* clear status message */
100133262Ssklower 			RestoreStatus();
100233262Ssklower 		    }
100333262Ssklower 		}
100433263Ssklower /*
100533263Ssklower     Sun Sep 20 18:39:40 PDT 1987
100633263Ssklower     Additional else necessary: if we get the final pagecount
100733263Ssklower     information here from the printer, store it in the given
100833263Ssklower     array pbuf[].
100933263Ssklower */
101033263Ssklower 		else if (match = FindPattern(cp, linebuf, "%%[ pagecount: ")) {
101133263Ssklower 		    /* fill pbuf */
101233263Ssklower 		    strcpy(pbuf, linebuf);
101333263Ssklower 		    debugp((stderr, "%s: 'premature' final pagecount read = '%s'\n", prog, pbuf));
101433263Ssklower 		}
101533262Ssklower 		else {
101633262Ssklower 		    /* message not for us */
101733262Ssklower 		    fprintf(jobout,"%s",linebuf);
101833262Ssklower 		    VOIDC fflush(jobout);
101933262Ssklower 		    st = normal;
102033262Ssklower 		    break;
102133262Ssklower 		}
102233262Ssklower 	    }
102333262Ssklower 	    break;
102433262Ssklower 	default:
102533262Ssklower 	    fprintf(stderr,"bad case;\n");
102633262Ssklower     }
102733262Ssklower     return;
102833262Ssklower }
102933262Ssklower 
103033262Ssklower /* backup "status" message file in ".status",
103133262Ssklower  * in case there is a PrinterError
103233262Ssklower  */
103333262Ssklower 
BackupStatus(file1,file2)103433262Ssklower private BackupStatus(file1, file2)
103533262Ssklower char *file1, *file2;
103633262Ssklower {
103733262Ssklower     register int fd1, fd2;
103833262Ssklower     char buf[BUFSIZ];
103933262Ssklower     int cnt;
104033262Ssklower 
104133262Ssklower     VOIDC umask(0);
104233262Ssklower     fd1 = open(file1, O_WRONLY|O_CREAT, 0664);
104333262Ssklower     if ((fd1 < 0) || (flock(fd1,LOCK_EX) < 0)) {
104433262Ssklower 	VOIDC unlink(file1);
104533262Ssklower 	VOIDC flock(fd1,LOCK_UN);
104633262Ssklower 	VOIDC close(fd1);
104733262Ssklower 	fd1 = open(file1, O_WRONLY|O_CREAT, 0664);
104833262Ssklower     }
104933262Ssklower     if ((fd1 < 0) || (flock(fd1,LOCK_EX) <0)) {
105033262Ssklower 	fprintf(stderr, "%s: writing %s:\n",prog,file1);
105133262Ssklower 	perror(prog);
105233262Ssklower 	VOIDC close(fd1);
105333262Ssklower 	return;
105433262Ssklower     }
105533262Ssklower     VOIDC ftruncate(fd1,0);
105633262Ssklower     if ((fd2 = open(file2, O_RDONLY,0)) < 0) {
105733262Ssklower 	fprintf(stderr, "%s: error reading %s:\n", prog, file2);
105833262Ssklower 	perror(prog);
105933262Ssklower 	VOIDC close(fd1);
106033262Ssklower 	return;
106133262Ssklower     }
106233262Ssklower     cnt = read(fd2,buf,BUFSIZ);
106333262Ssklower     VOIDC write(fd1,buf,cnt);
106433262Ssklower     VOIDC flock(fd1,LOCK_UN);
106533262Ssklower     VOIDC close(fd1);
106633262Ssklower     VOIDC close(fd2);
106733262Ssklower }
106833262Ssklower 
106933262Ssklower /* restore the "status" message from the backed-up ".status" copy */
RestoreStatus()107033262Ssklower private RestoreStatus() {
107133262Ssklower     BackupStatus("status",".status");
107233262Ssklower }
107333262Ssklower 
107433262Ssklower /* report PrinterError via "status" message file */
Status(msg)107533262Ssklower private Status(msg)
107633262Ssklower register char *msg;
107733262Ssklower {
107833262Ssklower     register int fd;
107933262Ssklower     char msgbuf[100];
108033262Ssklower 
108133262Ssklower     if ((fd = open("status",O_WRONLY|O_CREAT,0664)) < 0) return;
108233262Ssklower     VOIDC ftruncate(fd,0);
108333262Ssklower     sprintf(msgbuf,"Printer Error: may need attention! (%s)\n\0",msg);
108433262Ssklower     VOIDC write(fd,msgbuf,strlen(msgbuf));
108533262Ssklower     VOIDC close(fd);
108633262Ssklower }
108733262Ssklower 
108833262Ssklower /* sending phase alarm handler for sender */
108933262Ssklower 
salarm()109033262Ssklower private VOID salarm() {
109133262Ssklower 
109233262Ssklower     debugp((stderr,"%s: AS %d %d %d\n",prog,oldprogress,progress,getstatus));
109333262Ssklower 
109433262Ssklower     /* if progress != oldprogress, we made some progress (sent something)
109533262Ssklower      * else, we had two alarms without sending anything...
109633262Ssklower      * It may be that a PrinterError has us stopped, or we are computing
109733262Ssklower      * for a long time (forever?) -- printer jobtimeout may help here
109833262Ssklower      * in any case, all we do is set the flag to get status...
109933262Ssklower      * this will help us clear printererror notification
110033262Ssklower      */
110133262Ssklower 
110233262Ssklower     oldprogress = progress;
110333262Ssklower     getstatus = TRUE;
110433262Ssklower 
110533262Ssklower     /* reset the alarm and return */
110633262Ssklower     VOIDC alarm(SENDALARM);
110733262Ssklower     return;
110833262Ssklower }
110933262Ssklower 
111033262Ssklower /* waiting phase alarm handler for sender */
111133262Ssklower 
walarm()111233262Ssklower private VOID walarm() {
111333262Ssklower     static int acount = 0;
111433262Ssklower 
111533262Ssklower     debugp((stderr,"%s: WA %d %d %d %d\n",
111633262Ssklower     	prog,acount,oldprogress,progress,getstatus));
111733262Ssklower 
111833262Ssklower     if ((oldprogress != progress) || (acount == 4)) {
111933262Ssklower 	getstatus = TRUE;
112033262Ssklower 	acount = 0;
112133262Ssklower 	oldprogress = progress;
112233262Ssklower     }
112333262Ssklower     else acount++;
112433262Ssklower 
112533262Ssklower     /* reset alarm */
112633262Ssklower     VOIDC alarm(WAITALARM);
112733262Ssklower 
112833262Ssklower     /* return to wait loop */
112933262Ssklower     longjmp(dwait, 0);
113033262Ssklower }
113133262Ssklower 
113233262Ssklower /* final phase alarm handler for sender */
113333262Ssklower 
falarm()113433262Ssklower private VOID falarm() {
113533262Ssklower 
113633262Ssklower     debugp((stderr,"%s: FA %d %d %d\n",prog,oldprogress,progress,getstatus));
113733262Ssklower 
113833262Ssklower     /* no reason to count progress, just get status */
113933262Ssklower     if (!intrup) {
114033262Ssklower 	VOIDC write(fdsend, statusbuf, 1);
114133262Ssklower     }
114233262Ssklower     getstatus = FALSE;
114333262Ssklower 
114433262Ssklower     /* reset alarm */
114533262Ssklower     VOIDC alarm(WAITALARM);
114633262Ssklower     return;
114733262Ssklower }
114833262Ssklower 
114933262Ssklower /* initial interrupt handler - before communications begin, so
115033262Ssklower  * nothing to be sent to printer
115133262Ssklower  */
intinit()115233262Ssklower private VOID intinit() {
115333262Ssklower     long clock;
115433262Ssklower 
115533262Ssklower     /* get rid of banner file */
115633262Ssklower     VOIDC unlink(".banner");
115733262Ssklower 
115833262Ssklower     fprintf(stderr,"%s: abort (during setup)\n",prog);
115933262Ssklower     VOIDC fflush(stderr);
116033262Ssklower 
116133262Ssklower     /* these next two may be too cautious */
116233262Ssklower     VOIDC kill(0,SIGINT);
116333262Ssklower     while (wait((union wait *) 0) > 0);
116433262Ssklower 
116533262Ssklower     if (VerboseLog) {
116634945Sedward 	fprintf(stderr, "%s: end - %s", prog,
116734945Sedward 	    (time(&clock), ctime(&clock)));
116833262Ssklower 	VOIDC fflush(stderr);
116933262Ssklower     }
117033262Ssklower     exit(THROW_AWAY);
117133262Ssklower }
117233262Ssklower 
117333262Ssklower /* interrupt during sending phase to sender process */
117433262Ssklower 
intsend()117533262Ssklower private VOID intsend() {
117633262Ssklower     /* set flag */
117733262Ssklower     intrup = TRUE;
117833262Ssklower     longjmp(sendint, 0);
117933262Ssklower }
118033262Ssklower 
118133262Ssklower /* interrupt during waiting phase to sender process */
118233262Ssklower 
intwait()118333262Ssklower private VOID intwait() {
118433262Ssklower 
118533262Ssklower     intrup = TRUE;
118633262Ssklower 
118733262Ssklower     fprintf(stderr,"%s: abort (waiting)\n",prog);
118833262Ssklower     VOIDC fflush(stderr);
118933262Ssklower     if (ioctl(fdsend, TIOCFLUSH, &flg) || ioctl(fdsend, TIOCSTART, &flg)
119033262Ssklower     || (write(fdsend, abortbuf, 1) != 1)) {
119133262Ssklower 	fprintf(stderr, "%s: error in ioctl(fdsend):\n", prog);
119233262Ssklower 	perror(prog);
119333262Ssklower     }
119433262Ssklower 
119533262Ssklower     /* VOIDC alarm(2); /* force an alarm soon to get us out of wait! ? */
119633262Ssklower     longjmp(dwait, 0);
119733262Ssklower }
119833262Ssklower 
119933262Ssklower /* EMT for reverse filter, avoid printer timeout at the expense
120033262Ssklower  * of performance (sigh)
120133262Ssklower  */
120233262Ssklower 
reverseready()120333262Ssklower private VOID reverseready() {
120433262Ssklower     revdone = TRUE;
120533262Ssklower     longjmp(waitonreverse, 0);
120633262Ssklower }
120733262Ssklower 
120833262Ssklower /* EMT on startup to sender -- signalled by listener after first status
120933262Ssklower  * message received
121033262Ssklower  */
121133262Ssklower 
readynow()121233262Ssklower private VOID readynow() {
121333262Ssklower     goahead = TRUE;
121433262Ssklower     longjmp(startstatus, 0);
121533262Ssklower }
121633262Ssklower 
121733262Ssklower /* EMT on sending phase, hard EOF printer died! */
emtdead()121833262Ssklower private VOID emtdead() {
121933262Ssklower     VOIDC alarm(0);
122033262Ssklower     exit(THROW_AWAY);
122133262Ssklower }
122233262Ssklower 
122333262Ssklower /* EMT during waiting phase -- listener saw an EOF (^D) from printer */
122433262Ssklower 
emtdone()122533262Ssklower private VOID emtdone() {
122633262Ssklower     VOIDC alarm(0);
122733262Ssklower     gotemt = TRUE;
122833262Ssklower     longjmp(dwait, 0);
122933262Ssklower }
1230