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