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 168*34945Sedward private char abortbuf[] = {PS_INT, 0}; /* ^C abort */ 169*34945Sedward private char statusbuf[] = {PS_STATUS, 0}; /* ^T status */ 170*34945Sedward 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 18133262Ssklower private int flg = 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 198*34945Sedward main(argc, argv) 19933262Ssklower int argc; 200*34945Sedward char **argv; 20133262Ssklower { 20233262Ssklower register int cnt, wc; 203*34945Sedward 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 */ 225*34945Sedward BannerFirst = (cp = envget("BANNERFIRST")) != NULL ? atoi(cp) : 0; 226*34945Sedward BannerLast = (cp = envget("BANNERLAST")) != NULL ? atoi(cp) : 0; 227*34945Sedward VerboseLog = (cp = envget("VERBOSELOG")) != NULL ? atoi(cp) : 1; 228*34945Sedward reverse = envget("REVERSE"); /* name of the filter itself */ 229*34945Sedward prog = *argv; 230*34945Sedward while (*++argv) 231*34945Sedward if (**argv == '-') { 232*34945Sedward switch (argv[0][1]) { 23333262Ssklower case 'P': /* printer name */ 23433262Ssklower argc--; 235*34945Sedward pname = *++argv; 23633262Ssklower break; 23733262Ssklower 23833262Ssklower case 'n': /* user name */ 23933262Ssklower argc--; 240*34945Sedward name = *++argv; 24133262Ssklower break; 24233262Ssklower 24333262Ssklower case 'h': /* host */ 24433262Ssklower argc--; 245*34945Sedward host = *++argv; 24633262Ssklower break; 24733262Ssklower 24833262Ssklower case 'p': /* prog */ 24933262Ssklower argc--; 250*34945Sedward prog = *++argv; 25133262Ssklower break; 25233262Ssklower 25333262Ssklower case 'r': /* never reverse */ 25433262Ssklower argc--; 255*34945Sedward reverse = NULL; 25633262Ssklower break; 25733262Ssklower 25833262Ssklower default: /* unknown */ 259*34945Sedward fprintf(stderr, "%s: unknown option: %s\n", prog, *argv); 26033262Ssklower break; 26133262Ssklower } 262*34945Sedward } else 263*34945Sedward accountingfile = *argv; 264*34945Sedward 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 } 273*34945Sedward debugp((stderr, "%s: pid %d ppid %d\n", prog, getpid(), getppid())); 274*34945Sedward debugp((stderr, "%s: options BF %d BL %d VL %d R %s\n", prog, BannerFirst, 275*34945Sedward 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 280*34945Sedward switch (cnt = read(fileno(stdin), magic, 11)) { 281*34945Sedward case 11: 282*34945Sedward debugp((stderr, "%s: magic number is %11.11s\n", prog, magic)); 283*34945Sedward streamin = stdin; 284*34945Sedward if (strncmp(magic, "%!PS-Adobe-", 11) == 0) 285*34945Sedward goto go_ahead; 286*34945Sedward default: 287*34945Sedward if (strncmp(magic, "%!", 2) == 0) { 288*34945Sedward reverse = NULL; 289*34945Sedward goto go_ahead; 290*34945Sedward } 291*34945Sedward break; 292*34945Sedward case 0: 293*34945Sedward debugp((stderr, "%s: EOF reading magic number\n", prog)); 294*34945Sedward fprintf(stderr,"%s: empty file\n", prog); 295*34945Sedward goto badfile; 296*34945Sedward case -1: 297*34945Sedward debugp((stderr, "%s: error reading magic number\n", prog)); 298*34945Sedward fprintf(stderr,"%s: error reading magic number\n", prog); 299*34945Sedward badfile: 300*34945Sedward VOIDC fflush(stderr); 301*34945Sedward exit(THROW_AWAY); 30233262Ssklower } 303*34945Sedward /* 304*34945Sedward * here is where you might test for other file type 30533262Ssklower * e.g., PRESS, imPRESS, DVI, Mac-generated, etc. 30633262Ssklower */ 307*34945Sedward /* 308*34945Sedward * final sanity check on the text file, to guard 30933262Ssklower * against arbitrary binary data 31033262Ssklower */ 311*34945Sedward while (--cnt >= 0) 312*34945Sedward if (!isascii(magic[cnt]) || 313*34945Sedward !isprint(magic[cnt]) && !isspace(magic[cnt])) { 314*34945Sedward fprintf(stderr, "%s: spooled binary file rejected\n", prog); 31533262Ssklower VOIDC fflush(stderr); 316*34945Sedward sprintf(mybuf, "%s/bogusmsg.ps", envget("PSLIBDIR")); 317*34945Sedward if ((streamin = freopen(mybuf, "r", stdin)) == NULL) 31833262Ssklower exit(THROW_AWAY); 31933262Ssklower format = 1; 32033262Ssklower goto lastchance; 32133262Ssklower } 322*34945Sedward /* exec dumb formatter to make a listing */ 323*34945Sedward debugp((stderr, "formatting\n")); 324*34945Sedward format = 1; 325*34945Sedward VOIDC lseek(0, 0L, 0); 326*34945Sedward if (pipe(fdpipe) < 0) 327*34945Sedward pexit2(prog, "format pipe", THROW_AWAY); 328*34945Sedward if ((fpid = fork()) < 0) 329*34945Sedward pexit2(prog, "format fork", THROW_AWAY); 330*34945Sedward if (fpid == 0) { /* child */ 331*34945Sedward /* set up child stdout to feed parent stdin */ 332*34945Sedward if (close(1) < 0 || dup(fdpipe[1]) != 1 || 333*34945Sedward close(fdpipe[1]) < 0 || close(fdpipe[0]) < 0) 334*34945Sedward pexit2(prog, "format child", THROW_AWAY); 335*34945Sedward execl(envget("PSTEXT"), "pstext", pname, 0); 336*34945Sedward pexit2(prog, "format exec", THROW_AWAY); 33733262Ssklower } 338*34945Sedward /* parent continues, set up stdin to be pipe */ 339*34945Sedward if (close(0) < 0 || dup(fdpipe[0]) != 0 || 340*34945Sedward close(fdpipe[0]) < 0 || close(fdpipe[1]) < 0) 341*34945Sedward pexit2(prog, "format parent", THROW_AWAY); 342*34945Sedward /* fall through to spooler with new stdin */ 343*34945Sedward /* can't seek here but we should be at the right place */ 344*34945Sedward streamin = fdopen(0, "r"); 34533262Ssklower 346*34945Sedward go_ahead: 34733262Ssklower /* do page reversal if specified */ 348*34945Sedward if (reversing = reverse != NULL) { 349*34945Sedward debugp((stderr, "reversing\n")); 35033262Ssklower VOIDC setjmp(waitonreverse); 35133262Ssklower if (!revdone) { 35233262Ssklower VOIDC signal(SIGEMT, reverseready); 353*34945Sedward if (pipe(fdpipe) < 0) 354*34945Sedward pexit2(prog, "reverse pipe", THROW_AWAY); 355*34945Sedward if ((fpid = fork()) < 0) 356*34945Sedward pexit2(prog, "reverse fork", THROW_AWAY); 357*34945Sedward if (fpid == 0) { /* child */ 35833262Ssklower /* set up child stdout to feed parent stdin */ 359*34945Sedward if (close(1) < 0 || dup(fdpipe[1]) != 1 || 360*34945Sedward close(fdpipe[1]) < 0 || close(fdpipe[0]) < 0) 36133262Ssklower pexit2(prog, "reverse child", THROW_AWAY); 36233262Ssklower execl(reverse, "psrv", pname, 0); 363*34945Sedward pexit2(prog, "reverse exec", THROW_AWAY); 36433262Ssklower } 36533262Ssklower /* parent continues */ 366*34945Sedward if (close(0) < 0 || dup(fdpipe[0]) != 0 || 367*34945Sedward close(fdpipe[0]) < 0 || close(fdpipe[1]) < 0) 36833262Ssklower pexit2(prog, "reverse parent", THROW_AWAY); 36933262Ssklower /* fall through to spooler with new stdin */ 370*34945Sedward streamin = fdopen(0, "r"); 371*34945Sedward while (!revdone) 37233262Ssklower pause(); 37333262Ssklower } 37433262Ssklower VOIDC signal(SIGEMT, SIG_IGN); 375*34945Sedward debugp((stderr, "%s: reverse feeding\n", prog)); 37633262Ssklower } 37733262Ssklower 378*34945Sedward lastchance: 379*34945Sedward /* 380*34945Sedward * 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 */ 386*34945Sedward fdinput = fileno(streamin); /* the file to print */ 387*34945Sedward fdsend = fileno(stdout); /* the printer (write) */ 388*34945Sedward if ((fdlisten = dup(fdsend)) < 0) /* the printer (read) */ 389*34945Sedward pexit(prog, THROW_AWAY); 390*34945Sedward doactng = name && accountingfile && access(accountingfile, W_OK) == 0; 391*34945Sedward /* 392*34945Sedward * 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 */ 402*34945Sedward BackupStatus(".status", "status"); 403*34945Sedward if ((cpid = fork()) < 0) 404*34945Sedward pexit(prog, THROW_AWAY); 405*34945Sedward 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) 417*34945Sedward || (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 447*34945Sedward while (!goahead) { 448*34945Sedward debugp((stderr, "%s: get start status\n", prog)); 44933262Ssklower VOIDC write(fdsend, statusbuf, 1); 45033262Ssklower pause(); 451*34945Sedward if (goahead) 452*34945Sedward break; 45333262Ssklower /* if we get here, we got an alarm */ 45433262Ssklower ioctl(fdsend, TIOCFLUSH, &flg); 45533262Ssklower ioctl(fdsend, TIOCSTART, &flg); 456*34945Sedward 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) { 470*34945Sedward 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 502*34945Sedward while (!goahead) { 50333263Ssklower pause(); 504*34945Sedward if (goahead) 505*34945Sedward break; 50633263Ssklower /* if we get here, we got an alarm */ 50733263Ssklower ioctl(fdsend, TIOCFLUSH, &flg); 50833263Ssklower ioctl(fdsend, TIOCSTART, &flg); 509*34945Sedward 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! */ 529*34945Sedward if (!format && !reversing) { 530*34945Sedward 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 */ 621*34945Sedward 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) { 636*34945Sedward 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); 644*34945Sedward while ((wpid = wait(&status)) > 0) 645*34945Sedward ; 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) { 655*34945Sedward fprintf(stderr, "%s: end - %s", prog, 656*34945Sedward (time(&clock), ctime(&clock))); 65733262Ssklower VOIDC fflush(stderr); 65833262Ssklower } 659*34945Sedward RestoreStatus(); 660*34945Sedward exit(0); 661*34945Sedward } 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 */ 679*34945Sedward if ((cp = envget("JOBOUTPUT")) == NULL || 680*34945Sedward (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?) */ 690*34945Sedward 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 */ 696*34945Sedward continue; 69733262Ssklower /* RestoreStatus(); */ 69833262Ssklower /* exit(TRY_AGAIN); */ 69933262Ssklower } 700*34945Sedward if ((r & 0377) == '\n') 701*34945Sedward break; 70233262Ssklower *pb++ = r; 70333262Ssklower } 70433262Ssklower *pb = 0; 705*34945Sedward debugp((stderr, "%s: initial status - %s\n", prog, pbuf)); 70633262Ssklower if (strcmp(pbuf, "%%[ status: idle ]%%\r") != 0) { 707*34945Sedward 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 } 729*34945Sedward if ((r&0377) == PS_EOF) 730*34945Sedward 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)); 767*34945Sedward if ((r&0377) == PS_EOF) 768*34945Sedward break; 769*34945Sedward 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 } 817*34945Sedward if ((r&0377) == PS_EOF) 818*34945Sedward 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 */ 851*34945Sedward /* no need to close files */ 852*34945Sedward exit(0); 85333262Ssklower } 85433262Ssklower } 85533262Ssklower 85633262Ssklower /* send the file ".banner" */ 85733262Ssklower private SendBanner() 85833262Ssklower { 85933262Ssklower register int banner; 86033262Ssklower int cnt; 86133262Ssklower char buf[BUFSIZ]; 86233262Ssklower 863*34945Sedward if ((banner = open(".banner", O_RDONLY|O_NDELAY, 0)) < 0) 864*34945Sedward return; 865*34945Sedward while ((cnt = read(banner, buf, sizeof buf)) > 0) 866*34945Sedward VOIDC write(fdsend, buf, cnt); 86733262Ssklower VOIDC close(banner); 86833796Skarels VOIDC unlink(".banner"); 86933262Ssklower } 87033262Ssklower 87133262Ssklower /* search backwards from p in start for 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 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 } 92533262Ssklower 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; 93833262Ssklower if (c == '\]') st = close1; 93933262Ssklower break; 94033262Ssklower case close1: 94133262Ssklower *cp++ = c; 94233262Ssklower switch (c) { 94333262Ssklower case '%': st = close2; break; 94433262Ssklower 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; 95233262Ssklower 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; 96033262Ssklower 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; 96833262Ssklower 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 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 */ 107033262Ssklower private RestoreStatus() { 107133262Ssklower BackupStatus("status",".status"); 107233262Ssklower } 107333262Ssklower 107433262Ssklower /* report PrinterError via "status" message file */ 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 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 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 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 */ 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) { 1166*34945Sedward fprintf(stderr, "%s: end - %s", prog, 1167*34945Sedward (time(&clock), ctime(&clock))); 116833262Ssklower VOIDC fflush(stderr); 116933262Ssklower } 117033262Ssklower exit(THROW_AWAY); 117133262Ssklower } 117233262Ssklower 117333262Ssklower /* interrupt during sending phase to sender process */ 117433262Ssklower 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 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 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 121233262Ssklower private VOID readynow() { 121333262Ssklower goahead = TRUE; 121433262Ssklower longjmp(startstatus, 0); 121533262Ssklower } 121633262Ssklower 121733262Ssklower /* EMT on sending phase, hard EOF printer died! */ 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 122533262Ssklower private VOID emtdone() { 122633262Ssklower VOIDC alarm(0); 122733262Ssklower gotemt = TRUE; 122833262Ssklower longjmp(dwait, 0); 122933262Ssklower } 1230