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