dplatt@coherent.uucp (Dave Platt) (02/13/88)
This posting contains a set of enhancements I've made to papif., the AppleTalk/LaserWriter "input filter" in the Columbia AppleTalk Package. There are two major enhancements: 1) If you have TranScript from Adobe, papif will filter "conforming" PostScript files through psrev, thus reversing the order in which the pages are printed. Net result: output is stacked as you'd like (page 1 on top). Page-reversal is performed for files produced by the pstext filter and for any user-supplied PostScript file that is "minimally conforming" (begins with "%!PS-Adobe-"); PostScript files that are not minimally conforming are not filtered through psrev. 2) Whether or not you have TranScript, you can now request the printing of a job-trailer page. The page contains the submitting user's name (printed several times at the top of the page) and a log of all of the major events that occurred during printing: job start, print start, the use of the pstext and/or psrev filters, and any printer errors that may have occurred. The trailer page will be printed even if a PostScript error in the job causes the LaserWriter to flush the remainder of the job (papif sends an EOF before sending the trailer). I've also added an optional "bogon" filter, which strips out the bogus control-D character(s) that the PC-NFS print spooler insists on adding. These context diffs are based on the predistribution release 4 of CAP, upgraded with the level 5, 6, and 7 patch sets in the current Columbia and SUMEX distributions. My sources here have had all of the bug.NNNN patches up through bug.0014 installed in them, and thus these diffs reflect the inclusion in papif.c of bug.0003 (removes the race condition in the IDLESTUFF) and the one-line addition from bug.0014 (the call to ATPSetResponseTimeout). If you have installed these bug patches already, you should install this diff file with "patch -N" so that patch doesn't reject the duplicate diffs. If you haven't installed the bug.NNNN patches, you can simply run patch on these diffs, and then comment out the ATPSetResponseTimeout call... but I _strongly_ suggest that you acquire and install the bug patches (especially 3, 5, and 14), as printer reliability will be much better once you've done so. [Special offer... if you haven't been able to get the bug files from Columbia or from phri, drop me a note and I'll mail you these three. This offer void if I end up being swamped with requests] Happy printing! Comments, suggestions, and bug-reports are welcome. Dave Platt UUCP: ...!{ames,sun,uunet}!coherent!dplatt Internet: coherent!dplatt@ames.arpa, ...@sun.com, ...@uunet.uu.net *** Makefile.patch7 Tue Feb 9 17:31:06 1988 --- Makefile Fri Feb 12 13:26:24 1988 *************** *** 2,8 **** # # If you have Transcript from Adobe for your laserWriter and want to # print text files, uncomment the next line and set the location properly ! # ADOBEPS="-DPSTEXT=\"/usr/local/lib/ps/pstext\"" # # Note: for papif, there is another define called "IDLESTUFF" which # handles a problem that some sites see in papif seeing a long standing --- 2,15 ---- # # If you have Transcript from Adobe for your laserWriter and want to # print text files, uncomment the next line and set the location properly ! #ADOBEPS='-DPSTEXT="/usr/local/lib/lw/pstext"' ! ! # If you have TranScript, have uncommented the ADOBEPS line above, and ! # wish to have conforming PostScript files printed last-page-first ! # so that they stack nicely, uncomment the next line and set the ! # location properly. ! #ADOBEREV='-DPSREVERSE="/usr/local/lib/lw/psrev"' ! # # Note: for papif, there is another define called "IDLESTUFF" which # handles a problem that some sites see in papif seeing a long standing *************** *** 10,15 **** --- 17,35 ---- # # Define SFLOWQ=1 in CFLAGS if you seem to have problems with your LW dropping # pkts + # + # Define TRAILER in CFLAGS if you want an end-of-job trailer page, which + # contains the user's name and gives a time-stamped log of the printing + # process (including any printer errors). + # + # Define BOGON in CFLAGS if you want the bogon (control-D) filter + # activated. This filter is provided for the benefit of PC-NFS, which + # insists on adding a control-D at the end of its headers. + # + # Define NOSTATUS in CFLAGS if you want to suppress the periodic + # status-checking code (some users find that it causes the printer + # to hang). + CFLAGS=-O LFLAGS= MAC_EFSSRC=/usr/src/local/mac/efs/src/mac *************** *** 69,75 **** cc ${LFLAGS} -o papif papif.o $(O) $(CAPLIB) papif.o: papif.c ! cc ${CFLAGS} ${ADOBEPS} -c papif.c # # look, looks, and pinger all have a common source --- 89,95 ---- cc ${LFLAGS} -o papif papif.o $(O) $(CAPLIB) papif.o: papif.c ! cc ${CFLAGS} ${ADOBEPS} ${ADOBEREV} -c papif.c # # look, looks, and pinger all have a common source *** papif.c.patch7 Tue Feb 9 17:33:03 1988 --- papif.c Fri Feb 12 13:31:01 1988 *************** *** 17,22 **** --- 17,24 ---- * July 5, 1986 CCKim Clean up * Aug 20, 1986 CCKim, Gave up on old version, use lwpr routines instead. * Nov 1986 croft, restart after "status: idle", call pstext + * Feb 1988 coherent!dplatt, add psrev, trailer page w/job log, + * control-D filter for PC-NFS. * */ *************** *** 30,35 **** --- 32,38 ---- #include <sys/param.h> #include <signal.h> #include <strings.h> + #include <ctype.h> #include <netat/appletalk.h> /* include appletalk definitions */ #include <netat/compat.h> *************** *** 45,50 **** --- 48,55 ---- int cno; int ppid; int pid; + int eof, rlen, rcomp, wcomp, paperr; + int spc, epc; char host[30],printer[30],user[30]; #define RFLOWQ 8 #ifndef SFLOWQ *************** *** 57,62 **** --- 62,78 ---- int xdebug = TRUE; char *ptime(); + #ifdef TRAILER; + struct status_msg + { + struct status_msg *flink; + char text[128]; + }; + + struct status_msg *status_head, *status_tail; + int errors_received; + #endif + /* quit gets called when we've been killed by lprm via SIGINT */ quit() { *************** *** 143,150 **** char *getlwname(); char *acctfile; - - int spc, epc; #ifdef DEBUG int abort(); #endif --- 159,164 ---- *************** *** 156,174 **** --- 170,197 ---- getargs(argc, argv, printer, user, host, &acctfile); lwname = getlwname(printer); /* based on this */ + if (lwname == NULL) { fprintf(stderr,"Cannot map name %s to LaserWriter name\n",printer); exit(lpd_REPRINT); } + #ifdef TRAILER + status_head = status_tail = (struct status_msg *) NULL; + errors_received = 0; + log_message("Job started."); + #endif + /* init cap */ abInit(xdebug); /* initialize appletalk driver */ nbpInit(); PAPInit(); /* init PAP printer routines */ + ATPSetResponseTimeout(sectotick(60*4)); /* set to 4 minutes */ /* log message */ fprintf(stderr,"PAPIF: Starting job for %s@%s at %s on printer %s\n", user,host,ptime(),lwname); + fflush(stderr); #ifdef DEBUG signal(SIGEMT, abort); *************** *** 184,189 **** --- 207,216 ---- cno = openlw(lwname); + #ifdef TRAILER + log_message("Printer open."); + #endif; + #ifndef NOSTATUS /* base runs status process */ if ((pid = fork()) < 0) { *************** *** 237,243 **** --- 264,272 ---- } do { abSleep(16, TRUE); + #ifndef NOSTATUS pstatus(status.StatusStr); + #endif } while (ocomp > 0); return(cno); } *************** *** 249,269 **** */ pstext() { ! char magic[2]; ! int fdpipe[2]; ! int fpid; ! if (read(fileno(stdin),magic,2) != 2) { perror("pstext setup: read"); quit(); } rewind(stdin); lseek(fileno(stdin), 0L, 0); ! if (strncmp(magic,"%!",2) == 0) /* postscript file? */ ! return; /* yes, no more to do */ ! /* else assume a text file and pipe thru pstext */ if (pipe(fdpipe) != 0) { ! perror("pstext setup: pipe"); quit(); } fpid = vfork(); --- 278,330 ---- */ pstext() { ! char magic[11]; ! if (read(fileno(stdin),magic,11) != 11) { perror("pstext setup: read"); quit(); } rewind(stdin); lseek(fileno(stdin), 0L, 0); ! if (strncmp(magic, "%!", 2) == 0) ! { /* It's PostScript; no need to filter through pstext */ ! #ifdef PSREVERSE ! if (strncmp(magic, "%!PS-Adobe-", 11) != 0) ! { ! return; /* Nonconforming PostScript; send as-received */ ! } ! #else ! return; ! #endif ! } ! else ! { ! fork_filter(PSTEXT); /* Not PostScript; filter it */ ! #ifdef TRAILER ! log_message("Text-to-PostScript filter open."); ! #endif ! } ! #ifdef PSREVERSE ! fork_filter(PSREVERSE); ! #ifdef TRAILER ! log_message("Page-reversal filter open."); ! #endif ! #endif ! } ! ! /* ! Run a filter program in a child process; diddle the descriptors so that ! the filter eats the parent process's former stdin, and pipes its output ! into the parent's new stdin. ! */ ! fork_filter(whatever) ! char *whatever; ! { ! int fdpipe[2]; ! int fpid; ! if (pipe(fdpipe) != 0) { ! perror("filter setup: pipe"); quit(); } fpid = vfork(); *************** *** 270,294 **** switch (fpid) { case 0: /* child */ if (dup2(fdpipe[1], fileno(stdout)) == -1) { ! perror("pstext setup: child dup2"); quit(); } close(fdpipe[1]); /* ignore errs */ close(fdpipe[0]); ! execl(PSTEXT, "pstext", 0); /* if we are here again, then... */ ! perror("pstext setup: child exec"); kill(ppid, SIGINT); quit(); break; case -1: ! perror("pstext setup: fork"); quit(); break; default: /* parent continues */ /* set up stdin to be pipe */ if (dup2(fdpipe[0],fileno(stdin)) == -1) { ! perror("pstext setup: parent dup2"); quit(); } close(fdpipe[1]); /* ignore errs */ --- 331,355 ---- switch (fpid) { case 0: /* child */ if (dup2(fdpipe[1], fileno(stdout)) == -1) { ! perror("filter setup: child dup2"); quit(); } close(fdpipe[1]); /* ignore errs */ close(fdpipe[0]); ! execl(whatever, "psfilter", 0); /* if we are here again, then... */ ! perror("filter setup: child exec"); kill(ppid, SIGINT); quit(); break; case -1: ! perror("filter setup: fork"); quit(); break; default: /* parent continues */ /* set up stdin to be pipe */ if (dup2(fdpipe[0],fileno(stdin)) == -1) { ! perror("filter setup: parent dup2"); quit(); } close(fdpipe[1]); /* ignore errs */ *************** *** 304,317 **** sendfile(cno) int cno; { ! int eof, rlen, rcomp, wcomp, paperr, err, fd; fd = fileno(stdin); /* post initial read from LW */ if ((paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp)) < 0) { fprintf(stderr,"PAPRead error %d\n",paperr); } ! wcomp = 0; /* this is the main read/write loop */ do { --- 365,395 ---- sendfile(cno) int cno; { ! int err, fd; + #ifdef TRAILER + static char trailercode[] = "\ngsave initgraphics /Helvetica findfont 36\ + scalefont setfont /gl 0 def 700 -36 400 {200 exch moveto gl setgray (%s)\ + show /gl gl .12 add def} for /Helvetica findfont 8 scalefont setfont 0\ + setgray\n"; + static char logcode[] = "50 %d moveto (%s) show\n"; + char trailercmd[256]; + enum {sendbody, sendEOF1, sendident, sendlog, sendEOF2} + trailer_phase = sendbody; + int log_offset = 350; + #endif + fd = fileno(stdin); /* post initial read from LW */ if ((paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp)) < 0) { fprintf(stderr,"PAPRead error %d\n",paperr); + fflush(stderr); } ! fprintf(stderr,"Sending file"); ! fflush(stderr); ! #ifdef TRAILER ! log_message("Printing starts."); ! #endif wcomp = 0; /* this is the main read/write loop */ do { *************** *** 318,331 **** --- 396,416 ---- if (rcomp <= 0) { if (rcomp != noErr) { fprintf(stderr,"PAPRead error %d\n",rcomp); + fflush(stderr); break; } else if (rlen > 0) { rbuf[rlen] = '\0'; fprintf(stderr,"%s",rbuf); + fflush(stderr); + #ifdef TRAILER + errors_received++; + log_message(rbuf); + #endif } paperr = PAPRead(cno, rbuf, &rlen, &eof, &rcomp); if (paperr < 0) { fprintf(stderr,"PAPRead error %d\n",paperr); + fflush(stderr); break; } } *************** *** 332,344 **** if (wcomp <= 0) { if (wcomp != noErr) { fprintf(stderr,"PAPWrite error %d\n",wcomp); kill(ppid, SIGINT); /* signal abort to parent */ } else { ! err = read(fd, buf, SBUFMAX); ! if (err > 0) ! if ((paperr = PAPWrite(cno, buf, err, FALSE, &wcomp)) < 0) break; } } abSleep(4, TRUE); /* wait a bit */ --- 417,509 ---- if (wcomp <= 0) { if (wcomp != noErr) { fprintf(stderr,"PAPWrite error %d\n",wcomp); + fflush(stderr); kill(ppid, SIGINT); /* signal abort to parent */ } else { ! #ifdef TRAILER ! switch (trailer_phase) ! { ! case sendbody: ! err = read(fd, buf, SBUFMAX); ! if (err != 0) break; ! trailer_phase = sendEOF1; ! case sendEOF1: ! sendPAPeof(); ! epc = getpagecount(cno); ! if (spc != -1 && epc != -1) ! { ! if (epc - spc == 1) ! { ! log_message("End-of-file; 1 page printed."); ! } ! else ! { ! sprintf(buf, "End-of-file; %d page printed.", epc-spc); ! log_message(buf); ! } ! } ! else ! { ! log_message("End-of-file."); ! } ! fprintf(stderr, " EOF... "); ! fflush(stderr); ! trailer_phase = sendident; ! case sendident: ! fprintf(stderr, " trailer"); ! fflush(stderr); ! log_message("Job completed."); ! if (strlen(user) > 0) ! { ! sprintf(buf, trailercode, user); ! } ! else ! { ! sprintf(buf, trailercode, "no name"); ! } ! err = strlen(buf); ! trailer_phase = sendlog; break; + case sendlog: + if (status_head != (struct status_msg *) NULL) + { + sprintf(buf, logcode, log_offset, status_head->text); + log_offset -= 9; + status_head = status_head->flink; + } + else + { + strcpy(buf, "showpage grestore\n"); + trailer_phase = sendEOF2; + } + err = strlen(buf); + break; + case sendEOF2: + err = 0; + break; + } + #else + err = read(fd, buf, SBUFMAX); + #endif + if (err > 0) + { + #ifdef BOGON + register int bogon_scan; + for (bogon_scan = 0; bogon_scan < err; bogon_scan++) + { + if (buf[bogon_scan] == '\004') + { + buf[bogon_scan] = '\n'; + } + } + #endif + if ((paperr = PAPWrite(cno, buf, err, FALSE, &wcomp)) < 0) + break; + fprintf(stderr, "."); + fflush(stderr); + } + } } abSleep(4, TRUE); /* wait a bit */ *************** *** 346,370 **** if (err < 0) /* this is a little overloaded */ perror("read"); ! paperr = PAPWrite(cno, NULL, 0, TRUE, &wcomp); /* send eof */ ! while (!eof) { /* wait for completion */ ! if (rcomp <= 0) { ! if (rcomp != noErr) { ! fprintf(stderr,"PAPRead error %d\n",rcomp); ! break; ! } else if (rlen > 0) { ! rbuf[rlen] = '\0'; ! fprintf(stderr,"%s",rbuf); ! } ! if (eof) break; ! PAPRead(cno, rbuf, &rlen, &eof, &rcomp); ! } ! abSleep(4,TRUE); ! } ! if (paperr != noErr) ! fprintf(stderr,"PAPWrite error %d\n",paperr); ! else ! do { abSleep(4, TRUE); } while (wcomp > 0); } /* --- 511,521 ---- if (err < 0) /* this is a little overloaded */ perror("read"); ! fprintf(stderr, " EOF... "); ! fflush(stderr); ! sendPAPeof(); ! fprintf(stderr, " fini.\n"); ! fflush(stderr); } /* *************** *** 527,533 **** AddrBlock addr; int hangup(); PAPStatusRec status; ! PAPHalfClose(cno); signal(SIGCHLD, hangup); addr.net = 0; /* sufficient */ --- 678,687 ---- AddrBlock addr; int hangup(); PAPStatusRec status; ! #ifdef IDLESTUFF ! int count = 0; ! #endif IDLESTUFF ! PAPHalfClose(cno); signal(SIGCHLD, hangup); addr.net = 0; /* sufficient */ *************** *** 542,557 **** cpyp2cstr(tmpbuf, status.StatusStr); if (strncmp("status: idle", tmpbuf, 12) == 0 || access("/tmp/papifidletest", F_OK) == 0) { ! fprintf(stderr,"PAPIF: status: idle bug; restarting\n"); ! fflush(stderr); ! unlink("/tmp/papifidletest"); ! for (i = 0 ; i < NSIG ; i++) ! signal(i, SIG_IGN); ! sprintf(retry, ! "(sleep 2;/etc/lpc abort %s;sleep 2;/etc/lpc start %s)&", ! printer, printer); ! system(retry); ! exit(lpd_REPRINT); } } #endif IDLESTUFF --- 696,716 ---- cpyp2cstr(tmpbuf, status.StatusStr); if (strncmp("status: idle", tmpbuf, 12) == 0 || access("/tmp/papifidletest", F_OK) == 0) { ! count++; ! if (count >= 3) { /* Idle 30 seconds? Could be made shorter. */ ! fprintf(stderr,"PAPIF: status: idle bug; restarting\n"); ! fflush(stderr); ! unlink("/tmp/papifidletest"); ! for (i = 0 ; i < NSIG ; i++) ! signal(i, SIG_IGN); ! sprintf(retry, ! "(sleep 2;/etc/lpc abort %s;sleep 2;/etc/lpc start %s)&", ! printer, printer); ! system(retry); ! exit(lpd_REPRINT); ! } ! } else { ! count = 0; /* once it goes non-idle; restart count */ } } #endif IDLESTUFF *************** *** 558,561 **** --- 717,800 ---- pstatus(status.StatusStr); sleep(10); /* update every 10 seconds */ } while (1); + } + + #ifdef TRAILER + + int + log_message(what) + char *what; + { + struct status_msg *msg; + char text[512], *next; + + msg = (struct status_msg *) malloc(sizeof(struct status_msg)); + msg->flink = (struct status_msg *) NULL; + + if (status_tail) + { + status_tail->flink = msg; + } + else + { + status_head = msg; + } + status_tail = msg; + + strcpy(text, ptime()); + strcat(text, " "); + + next = text+strlen(text); + + while (*what) + { + if (*what >= ' ') + { + if (!isspace(*what) && !isalnum(*what)) /* Overkill, but WTF */ + { + *next++ = '\\'; + } + *next++ = *what; + } + what++; + } + + *next = '\0'; + + strncpy(msg->text, text, sizeof msg->text); + } + #endif + + int + sendPAPeof() + { + + paperr = PAPWrite(cno, NULL, 0, TRUE, &wcomp); /* send eof */ + while (!eof) { /* wait for completion */ + if (rcomp <= 0) { + if (rcomp != noErr) { + fprintf(stderr,"PAPRead error %d\n",rcomp); + fflush(stderr); + break; + } else if (rlen > 0) { + rbuf[rlen] = '\0'; + fprintf(stderr,"%s",rbuf); + fflush(stderr); + #ifdef TRAILER + log_message(rbuf); + errors_received++; + #endif + } + if (eof) break; + PAPRead(cno, rbuf, &rlen, &eof, &rcomp); + } + abSleep(4,TRUE); + } + if (paperr != noErr) + { + fprintf(stderr,"PAPWrite error %d\n",paperr); + fflush(stderr); + } + else + do { abSleep(4, TRUE); } while (wcomp > 0); } -- Dave Platt UUCP: ...!{ames,sun,uunet}!coherent!dplatt Internet: coherent!dplatt@ames.arpa, ...@sun.com, ...@uunet.uu.net