1 #ifndef lint 2 static char Notice[] = "Copyright (c) 1985 Adobe Systems Incorporated"; 3 static char *RCSID="$Header: pscomm.bsd,v 2.1 85/11/24 11:50:16 shore Rel $"; 4 #endif 5 /* pscomm.c 6 * 7 * Copyright (C) 1985 Adobe Systems Incorporated 8 * 9 * 4.2BSD lpr/lpd communications filter for PostScript printers 10 * (formerly "psif" in TranScript release 1.0) 11 * 12 * pscomm is the general communications filter for 13 * sending files to a PostScript printer (e.g., an Apple LaserWriter, 14 * QMS PostScript printer, or Linotype PostScript typesetter) 15 * via RS232 lines. It does page accounting, error handling/reporting, 16 * job logging, banner page printing, etc. 17 * It observes (parts of) the PostScript file structuring conventions. 18 * In particular, it distinguishes between PostScript files (beginning 19 * with the "%!" magic number) -- which are shipped to the printer -- 20 * and text files (no magic number) which are formatted and listed 21 * on the printer. Files which begin with "%!PS-Adobe-" may be 22 * page-reversed if the target printer has that option specified. 23 * 24 * depending on the values of BANNERFIRST and BANNERLAST, 25 * pscomm looks for a file named ".banner", (created by the "of" filter) 26 * in the current working directory and ships it to the printer also. 27 * 28 * pscomm gets called with: 29 * stdin == the file to print (may be a pipe!) 30 * stdout == the printer 31 * stderr == the printer log file 32 * cwd == the spool directory 33 * argv == set up by interface shell script: 34 * filtername -P printer 35 * -p filtername 36 * [-r] (don't ever reverse) 37 * -n login 38 * -h host 39 * [accntfile] 40 * 41 * environ == various environment variable effect behavior 42 * VERBOSELOG - do verbose log file output 43 * BANNERFIRST - print .banner before job 44 * BANNERLAST - print .banner after job 45 * REVERSE - page reversal filter program 46 * (no reversal if null or missing) 47 * PSLIBDIR - transcript library directory 48 * PSTEXT - simple text formatting filter 49 * JOBOUTPUT - file for actual printer stream 50 * output (if defined) 51 * 52 * pscomm depends on certain additional features of the 4.2BSD spooling 53 * architecture. In particular it assumes that the printer status file 54 * has the default name (./status) and it uses this file to communicate 55 * printer error status information to the user -- the contents of the 56 * status file gets incorporated in "lpq" and "lpc status" messages. 57 * 58 * Edit History: 59 * Andrew Shore: Sat Nov 16 11:59:58 1985 60 * End Edit History. 61 * 62 * RCSLOG: 63 * $Log: pscomm.bsd,v $ 64 * Revision 2.1 85/11/24 11:50:16 shore 65 * Product Release 2.0 66 * 67 * Revision 1.1 85/11/20 00:35:21 shore 68 * Initial revision 69 * 70 * Revision 1.2 85/05/14 11:25:29 shore 71 * better support for BANNERLAST, still buggy though 72 * 73 * 74 */ 75 76 #include <ctype.h> 77 #include <setjmp.h> 78 #include <sgtty.h> 79 #include <signal.h> 80 #include <stdio.h> 81 #include <strings.h> 82 83 #include <sys/file.h> 84 #include <sys/ioctl.h> 85 #include <sys/time.h> 86 #include <sys/resource.h> 87 #include <sys/wait.h> 88 #include <sys/types.h> 89 #include <sys/stat.h> 90 91 #include "transcript.h" 92 #include "psspool.h" 93 94 #ifdef BDEBUG 95 #define debugp(x) {fprintf x ; (void) fflush(stderr);} 96 #else 97 #define debugp(x) 98 #endif BDEBUG 99 100 /* 101 * the following string is sent to the printer when we want it to 102 * report its current pagecount (for accounting) 103 */ 104 105 private char *getpages = "\n(%%%%[ pagecount: )print \ 106 statusdict/pagecount get exec( )cvs print( ]%%%%)= flush\n%s"; 107 108 private jmp_buf waitonreverse, startstatus, dwait, sendint; 109 110 private char *prog; /* invoking program name */ 111 private char *name; /* user login name */ 112 private char *host; /* host name */ 113 private char *pname; /* printer name */ 114 private char *accountingfile; /* file for printer accounting */ 115 private int doactng; /* true if we can do accounting */ 116 private int progress, oldprogress; /* finite progress counts */ 117 private int getstatus = FALSE; 118 private int revdone = FALSE; /* reverse done, send new */ 119 private int goahead = FALSE; /* got initial status back */ 120 private int gotemt = FALSE; /* got ^D ack from listener */ 121 private int sendend = TRUE; /* send an ^D */ 122 123 private char *bannerfirst; 124 private char *bannerlast; 125 private char *verboselog; 126 private char *reverse; 127 private int BannerFirst; 128 private int BannerLast; 129 private int VerboseLog; 130 131 private int fpid = 0; /* formatter pid */ 132 private int cpid = 0; /* listener pid */ 133 134 private int intrup = FALSE; /* interrupt flag */ 135 136 private char abortbuf[] = "\003"; /* ^C abort */ 137 private char statusbuf[] = "\024"; /* ^T status */ 138 private char eofbuf[] = "\004"; /* ^D end of file */ 139 140 private char EOFerr[] = "%s: unexpected EOF from printer (%s)!\n"; 141 142 /* global file descriptors (avoid stdio buffering!) */ 143 private int fdsend; /* to printer (from stdout) */ 144 private int fdlisten; /* from printer (same tty line) */ 145 private int fdinput; /* file to print (from stdin) */ 146 147 private FILE *jobout; /* special printer output log */ 148 149 private int flg = FREAD|FWRITE; /* ioctl FLUSH arg */ 150 151 152 extern char *getenv(); 153 154 private VOID intinit(); 155 private VOID intsend(); 156 private VOID intwait(); 157 private VOID salarm(); 158 private VOID walarm(); 159 private VOID falarm(); 160 private VOID reverseready(); 161 private VOID readynow(); 162 private VOID emtdead(); 163 private VOID emtdone(); 164 private char *FindPattern(); 165 166 #define SENDALARM 90 167 #define WAITALARM 30 168 169 main(argc,argv) 170 int argc; 171 char *argv[]; 172 { 173 register char *cp; 174 register int cnt, wc; 175 register char *mbp; 176 177 char **av; 178 long clock; /* for log timestamp */ 179 char magic[11]; /* first few bytes of stdin ?magic number and type */ 180 int noReverse = 0; /* flag if we should never page reverse */ 181 int canReverse = 0;/* flag if we can page-reverse the ps file */ 182 int reversing = 0; 183 FILE *streamin; 184 185 char mybuf[BUFSIZ]; 186 int wpid; 187 union wait status; 188 int fdpipe[2]; 189 int format = 0; 190 int i; 191 192 VOIDC signal(SIGINT, intinit); 193 VOIDC signal(SIGHUP, intinit); 194 VOIDC signal(SIGQUIT, intinit); 195 VOIDC signal(SIGTERM, intinit); 196 197 /* parse command-line arguments */ 198 /* the argv (see header comments) comes from the spooler daemon */ 199 /* itself, so it should be canonical, but at least one 4.2-based */ 200 /* system uses -nlogin -hhost (insead of -n login -h host) so I */ 201 /* check for both */ 202 203 av = argv; 204 prog = *av; 205 206 while (--argc) { 207 if (*(cp = *++av) == '-') { 208 switch (*(cp + 1)) { 209 case 'P': /* printer name */ 210 argc--; 211 pname = *(++av); 212 break; 213 214 case 'n': /* user name */ 215 argc--; 216 name = *(++av); 217 break; 218 219 case 'h': /* host */ 220 argc--; 221 host = *(++av); 222 break; 223 224 case 'p': /* prog */ 225 argc--; 226 prog = *(++av); 227 break; 228 229 case 'r': /* never reverse */ 230 argc--; 231 noReverse = 1; 232 break; 233 234 default: /* unknown */ 235 fprintf(stderr,"%s: unknown option: %s\n",prog,cp); 236 break; 237 } 238 } 239 else 240 accountingfile = cp; 241 } 242 243 debugp((stderr,"args: %s %s %s %s\n",prog,host,name,accountingfile)); 244 245 /* do printer-specific options processing */ 246 247 VerboseLog = 1; 248 BannerFirst = BannerLast = 0; 249 reverse = NULL; 250 if (bannerfirst=envget("BANNERFIRST")) { 251 BannerFirst=atoi(bannerfirst); 252 } 253 if (bannerlast=envget("BANNERLAST")) { 254 BannerLast=atoi(bannerlast); 255 } 256 if (verboselog=envget("VERBOSELOG")) { 257 VerboseLog=atoi(verboselog); 258 } 259 if (!noReverse) { 260 reverse=envget("REVERSE"); /* name of the filter itself */ 261 } 262 263 if (VerboseLog) { 264 fprintf(stderr, "%s: %s:%s %s start - %s", prog, host, name, pname, 265 (VOIDC time(&clock), ctime(&clock))); 266 VOIDC fflush(stderr); 267 } 268 debugp((stderr,"%s: pid %d ppid %d\n",prog,getpid(),getppid())); 269 debugp((stderr,"%s: options BF %d BL %d VL %d R %s\n",prog,BannerFirst, 270 BannerLast, VerboseLog, ((reverse == NULL) ? "norev": reverse))); 271 272 /* IMPORTANT: in the case of cascaded filters, */ 273 /* stdin may be a pipe! (and hence we cannot seek!) */ 274 275 if ((cnt = read(fileno(stdin),magic,11)) != 11) goto badfile; 276 debugp((stderr,"%s: magic number is %11.11s\n",prog,magic)); 277 streamin = stdin; 278 279 if (strncmp(magic,"%!PS-Adobe-",11) == 0) { 280 canReverse = TRUE; 281 goto go_ahead; 282 } 283 else if (strncmp(magic,"%!",2) == 0) { 284 canReverse = FALSE; 285 goto go_ahead; 286 } 287 288 /* here is where you might test for other file type 289 * e.g., PRESS, imPRESS, DVI, Mac-generated, etc. 290 */ 291 292 /* final sanity check on the text file, to guard 293 * against arbitrary binary data 294 */ 295 296 for (i = 0; i < 11; i++) { 297 if (!isascii(magic[i]) || (!isprint(magic[i]) && !isspace(magic[i]))){ 298 fprintf(stderr,"%s: spooled binary file rejected\n",prog); 299 VOIDC fflush(stderr); 300 sprintf(mybuf,"%s/bogusmsg.ps",envget("PSLIBDIR")); 301 if ((streamin = freopen(mybuf,"r",stdin)) == NULL) { 302 exit(THROW_AWAY); 303 } 304 format = 1; 305 goto lastchance; 306 } 307 } 308 309 goto format_text; 310 311 badfile: 312 fprintf(stderr,"%s: bad magic number, EOF\n", prog); 313 VOIDC fflush(stderr); 314 exit(THROW_AWAY); 315 316 format_text: 317 /* exec dumb formatter to make a listing */ 318 debugp((stderr,"formatting\n")); 319 format = 1; 320 VOIDC lseek(0,0L,0); 321 rewind(stdin); 322 if (pipe (fdpipe)) pexit2(prog, "format pipe",THROW_AWAY); 323 if ((fpid = fork()) < 0) pexit2(prog, "format fork",THROW_AWAY); 324 if (fpid == 0) { /* child */ 325 /* set up child stdout to feed parent stdin */ 326 if (close(1) || (dup(fdpipe[1]) != 1) 327 || close(fdpipe[1]) || close(fdpipe[0])) { 328 pexit2(prog, "format child",THROW_AWAY); 329 } 330 execl(envget("PSTEXT"), "pstext", pname, 0); 331 pexit2(prog,"format exec",THROW_AWAY); 332 } 333 /* parent continues */ 334 /* set up stdin to be pipe */ 335 if (close(0) || (dup(fdpipe[0]) != 0) 336 || close(fdpipe[0]) || close(fdpipe[1])) { 337 pexit2(prog, "format parent",THROW_AWAY); 338 } 339 340 /* fall through to spooler with new stdin */ 341 /* can't seek here but we should be at the right place */ 342 streamin = fdopen(0,"r"); 343 canReverse = TRUE; /* we know we can reverse pstext output */ 344 345 go_ahead: 346 347 /* do page reversal if specified */ 348 if (reversing = ((reverse != NULL) && canReverse)) { 349 debugp((stderr,"reversing\n")); 350 VOIDC setjmp(waitonreverse); 351 if (!revdone) { 352 VOIDC signal(SIGEMT, reverseready); 353 if (pipe (fdpipe)) pexit2(prog, "reverse pipe", THROW_AWAY); 354 if ((fpid = fork()) < 0) pexit2(prog, "reverse fork", THROW_AWAY); 355 if (fpid == 0) { /* child */ 356 /* set up child stdout to feed parent stdin */ 357 if (close(1) || (dup(fdpipe[1]) != 1) 358 || close(fdpipe[1]) || close(fdpipe[0])) { 359 pexit2(prog, "reverse child", THROW_AWAY); 360 } 361 execl(reverse, "psrv", pname, 0); 362 pexit2(prog,"reverse exec",THROW_AWAY); 363 } 364 /* parent continues */ 365 if (close(0) || (dup(fdpipe[0]) != 0) 366 || close(fdpipe[0]) || close(fdpipe[1])) { 367 pexit2(prog, "reverse parent", THROW_AWAY); 368 } 369 /* fall through to spooler with new stdin */ 370 /* VOIDC lseek(0,0L,0); */ 371 streamin = fdopen(0,"r"); 372 373 while (TRUE) { 374 if (revdone) break; 375 pause(); 376 } 377 } 378 VOIDC signal(SIGEMT, SIG_IGN); 379 debugp((stderr,"%s: reverse feeding\n",prog)); 380 } 381 382 lastchance:; 383 384 /* establish an input stream from the printer -- 385 * the printcap entry specifies "rw" and we get 386 * invoked with stdout == the device, so we 387 * dup stdout, and reopen it for reading; 388 * this seems to work fine... 389 */ 390 391 fdinput = fileno(streamin); /* the file to print */ 392 fdsend = fileno(stdout); /* the printer (write) */ 393 394 if ((fdlisten = dup(fdsend)) < 0) /* the printer (read) */ 395 pexit(prog, THROW_AWAY); 396 397 doactng = name && accountingfile && (access(accountingfile, W_OK) == 0); 398 399 /* get control of the "status" message file. 400 * we copy the current one to ".status" so we can restore it 401 * on exit (to be clean). 402 * Our ability to use this is publicized nowhere in the 403 * 4.2 lpr documentation, so things might go bad for us. 404 * We will use it to report that printer errors condition 405 * has been detected, and the printer should be checked. 406 * Unfortunately, this notice may persist through 407 * the end of the print job, but this is no big deal. 408 */ 409 BackupStatus(".status","status"); 410 411 if ((cpid = fork()) < 0) pexit(prog, THROW_AWAY); 412 else if (cpid) {/* parent - sender */ 413 VOIDC setjmp(sendint); 414 415 if (intrup) { 416 /* we only get here if there was an interrupt */ 417 418 fprintf(stderr,"%s: abort (sending)\n",prog); 419 VOIDC fflush(stderr); 420 421 /* flush and restart output to printer, 422 * send an abort (^C) request and wait for the job to end 423 */ 424 if (ioctl(fdsend, TIOCFLUSH,&flg) || ioctl(fdsend, TIOCSTART,&flg) 425 || (write(fdsend, abortbuf, 1) != 1)) { 426 RestoreStatus(); 427 pexit(prog,THROW_AWAY); 428 } 429 debugp((stderr,"%s: sent interrupt - waiting\n",prog)); 430 intrup = 0; 431 goto donefile; /* sorry ewd! */ 432 } 433 434 VOIDC signal(SIGINT, intsend); 435 VOIDC signal(SIGHUP, intsend); 436 VOIDC signal(SIGQUIT, intsend); 437 VOIDC signal(SIGTERM, intsend); 438 VOIDC signal(SIGEMT, readynow); 439 440 progress = oldprogress = 0; /* finite progress on sender */ 441 getstatus = FALSE; /* prime the pump for fun FALSE; */ 442 443 VOIDC signal(SIGALRM, salarm); /* sending phase alarm */ 444 VOIDC alarm(SENDALARM); /* schedule an alarm/timeout */ 445 446 /* loop, trying to send a ^T to get printer status 447 * We will hang here (and post a message) if the printer 448 * is unreachable. Eventually, we will succeed, the listener 449 * will see the status report, signal us, and we will proceed 450 */ 451 452 cnt = 1; 453 VOIDC setjmp(startstatus); 454 455 while (TRUE) { 456 if (goahead) break; 457 debugp((stderr,"%s: get start status\n",prog)); 458 VOIDC write(fdsend, statusbuf, 1); 459 pause(); 460 if (goahead) break; 461 /* if we get here, we got an alarm */ 462 ioctl(fdsend, TIOCFLUSH, &flg); 463 ioctl(fdsend, TIOCSTART, &flg); 464 sprintf(mybuf, "Not Responding for %d minutes", 465 (cnt * SENDALARM+30)/60); 466 Status(mybuf); 467 alarm(SENDALARM); 468 cnt++; 469 } 470 471 VOIDC signal(SIGEMT, emtdead); /* now EMTs mean printer died */ 472 473 RestoreStatus(); 474 debugp((stderr,"%s: printer responding\n",prog)); 475 476 /* initial page accounting (BEFORE break page) */ 477 if (doactng) { 478 sprintf(mybuf, getpages, "\004"); 479 VOIDC write(fdsend, mybuf, strlen(mybuf)); 480 progress++; 481 } 482 483 /* initial break page ? */ 484 if (BannerFirst) { 485 SendBanner(); 486 progress++; 487 } 488 489 /* ship the magic number! */ 490 if ((!format) && (!reversing)) { 491 VOIDC write(fdsend,magic,11); 492 progress++; 493 } 494 495 /* now ship the rest of the file */ 496 497 VOIDC alarm(SENDALARM); /* schedule an alarm */ 498 499 while ((cnt = read(fdinput, mybuf, sizeof mybuf)) > 0) { 500 /* VOIDC alarm(SENDALARM); /* we made progress, reset alarm */ 501 if (intrup == TRUE) break; 502 503 /* get status every other time */ 504 if (getstatus) { 505 VOIDC write(fdsend, statusbuf, 1); 506 getstatus = FALSE; 507 progress++; 508 } 509 mbp = mybuf; 510 while ((cnt > 0) && ((wc = write(fdsend, mbp, cnt)) != cnt)) { 511 /* this seems necessary but not sure why */ 512 if (wc < 0) { 513 fprintf(stderr,"%s: error writing to printer:\n",prog); 514 perror(prog); 515 RestoreStatus(); 516 sleep(10); 517 exit(TRY_AGAIN); 518 } 519 mbp += wc; 520 cnt -= wc; 521 progress++; 522 } 523 progress++; 524 } 525 if (cnt < 0) { 526 fprintf(stderr,"%s: error reading from stdin: \n", prog); 527 perror(prog); 528 RestoreStatus(); 529 sleep(10); 530 exit(TRY_AGAIN); /* kill the listener? */ 531 } 532 533 534 donefile:; 535 536 sendend = 1; 537 538 VOIDC setjmp(dwait); 539 540 if (sendend && !gotemt) { 541 542 VOIDC signal(SIGEMT, emtdone); 543 544 debugp((stderr,"%s: done sending\n",prog)); 545 546 /* now send the PostScript EOF character */ 547 VOIDC write(fdsend, eofbuf, 1); 548 sendend = 0; 549 progress++; 550 551 VOIDC signal(SIGINT, intwait); 552 VOIDC signal(SIGHUP, intwait); 553 VOIDC signal(SIGQUIT, intwait); 554 VOIDC signal(SIGTERM, intwait); 555 556 VOIDC signal(SIGALRM, walarm); 557 VOIDC alarm(WAITALARM); 558 getstatus = TRUE; 559 } 560 561 /* wait to sync with listener EMT signal 562 * to indicate it got an EOF from the printer 563 */ 564 while (TRUE) { 565 if (gotemt) break; 566 if (getstatus) { 567 VOIDC write(fdsend, statusbuf, 1); 568 getstatus = FALSE; 569 } 570 debugp((stderr,"waiting e%d i%d %d %d\n", 571 gotemt,intrup,wpid,status)); 572 wpid = wait(&status); 573 if (wpid == -1) break; 574 } 575 576 VOIDC signal(SIGALRM, falarm); 577 VOIDC alarm(WAITALARM); 578 579 /* final break page ? */ 580 if (BannerLast) { 581 SendBanner(); 582 progress++; 583 } 584 if (BannerFirst) VOIDC unlink(".banner"); 585 586 /* final page accounting */ 587 if (doactng) { 588 sprintf(mybuf, getpages, ""); 589 VOIDC write(fdsend, mybuf, strlen(mybuf)); 590 progress++; 591 } 592 /* if we sent anything, finish it off */ 593 if (BannerLast || doactng) { 594 VOIDC write(fdsend, eofbuf, 1); 595 progress++; 596 } 597 598 /* wait for listener to die */ 599 VOIDC setjmp(dwait); 600 while ((wpid = wait(&status)) > 0); 601 VOIDC alarm(0); 602 VOIDC signal(SIGINT, SIG_IGN); 603 VOIDC signal(SIGHUP, SIG_IGN); 604 VOIDC signal(SIGQUIT, SIG_IGN); 605 VOIDC signal(SIGTERM, SIG_IGN); 606 VOIDC signal(SIGEMT, SIG_IGN); 607 debugp((stderr,"w2: s%lo p%d = p%d\n", status, wpid, cpid)); 608 609 if (VerboseLog) { 610 fprintf(stderr,"%s: end - %s",prog, 611 (VOIDC time(&clock),ctime(&clock))); 612 VOIDC fflush(stderr); 613 } 614 RestoreStatus(); 615 exit(0); 616 } 617 else {/* child - listener */ 618 register FILE *psin; 619 register int r; 620 621 char pbuf[BUFSIZ]; /* buffer for pagecount info */ 622 char *pb; /* pointer for above */ 623 int pc1, pc2; /* page counts before and after job */ 624 int sc; /* pattern match count for sscanf */ 625 char *outname; /* file name for job output */ 626 int havejobout = FALSE; /* flag if jobout != stderr */ 627 int ppid; /* parent process id */ 628 629 VOIDC signal(SIGINT, SIG_IGN); 630 VOIDC signal(SIGHUP, SIG_IGN); 631 VOIDC signal(SIGQUIT, SIG_IGN); 632 VOIDC signal(SIGTERM, SIG_IGN); 633 VOIDC signal(SIGALRM, SIG_IGN); 634 635 ppid = getppid(); 636 637 /* get jobout from environment if there, otherwise use stderr */ 638 if (((outname = envget("JOBOUTPUT")) == NULL) 639 || ((jobout = fopen(outname,"w")) == NULL)) { 640 jobout = stderr; 641 } 642 else havejobout = TRUE; 643 644 pc1 = pc2 = -1; /* bogus initial values */ 645 if ((psin = fdopen(fdlisten, "r")) == NULL) { 646 RestoreStatus(); 647 pexit(prog, THROW_AWAY); 648 } 649 650 /* listen for first status (idle?) */ 651 pb = pbuf; 652 *pb = '\0'; 653 while (TRUE) { 654 r = getc(psin); 655 if (r == EOF) { 656 fprintf(stderr, EOFerr, prog, "startup"); 657 VOIDC fflush(stderr); 658 sleep(20); /* printer may be coming up */ 659 /* RestoreStatus(); */ 660 /* exit(TRY_AGAIN); */ 661 } 662 if ((r & 0377) == '\n') break; /* newline */ 663 *pb++ = r; 664 } 665 *pb = 0; 666 if (strcmp(pbuf, "%%[ status: idle ]%%\r") != 0) { 667 fprintf(stderr,"%s: initial status - %s\n",prog,pbuf); 668 VOIDC fflush(stderr); 669 } 670 671 /* flush input state and signal sender that we heard something */ 672 ioctl(fdlisten, TIOCFLUSH, &flg); 673 674 VOIDC kill(ppid,SIGEMT); 675 676 /* listen for first pagecount */ 677 if (doactng) { 678 pb = pbuf; 679 *pb = '\0'; 680 while (TRUE) { 681 r = getc(psin); 682 if (r == EOF) { 683 fprintf(stderr, EOFerr, prog, "accounting1"); 684 VOIDC fflush(stderr); 685 RestoreStatus(); 686 sleep(10); /* give interface a chance */ 687 exit(TRY_AGAIN); 688 } 689 if ((r&0377) == 004) break; /* PS_EOF */ 690 *pb++ = r; 691 } 692 *pb = '\0'; 693 694 if (pb = FindPattern(pb, pbuf, "%%[ pagecount: ")) { 695 sc = sscanf(pb, "%%%%[ pagecount: %d ]%%%%\r", &pc1); 696 } 697 if ((pb == NULL) || (sc != 1)) { 698 fprintf(stderr, "%s: accounting error 1 (%s)\n", prog,pbuf); 699 VOIDC fflush(stderr); 700 } 701 debugp((stderr,"%s: accounting 1 (%s)\n",prog,pbuf)); 702 } 703 704 /* listen for the user job */ 705 while (TRUE) { 706 r = getc(psin); 707 if ((r&0377) == 004) break; /* PS_EOF */ 708 else if (r == EOF) { 709 VOIDC fclose(psin); 710 fprintf(stderr, EOFerr, prog, "job"); 711 VOIDC fflush(stderr); 712 RestoreStatus(); 713 VOIDC kill(ppid,SIGEMT); 714 exit(THROW_AWAY); 715 } 716 GotChar(r); 717 } 718 719 /* let sender know we saw the end of the job */ 720 /* sync - wait for sender to restart us */ 721 722 debugp((stderr,"%s: listener saw eof, signaling\n",prog)); 723 724 VOIDC kill(ppid,SIGEMT); 725 726 /* now get final page count */ 727 if (doactng) { 728 pb = pbuf; 729 *pb = '\0'; /* ignore the previous pagecount */ 730 while (TRUE) { 731 r = getc(psin); 732 if (r == EOF) { 733 fprintf(stderr, EOFerr, prog, "accounting2"); 734 VOIDC fflush(stderr); 735 RestoreStatus(); 736 sleep(10); 737 exit(THROW_AWAY); /* what else to do? */ 738 } 739 if ((r&0377) == 004) break; /* PS_EOF */ 740 *pb++ = r; 741 } 742 *pb = '\0'; 743 debugp((stderr,"%s: accounting 2 (%s)\n",prog,pbuf)); 744 if (pb = FindPattern(pb, pbuf, "%%[ pagecount: ")) { 745 sc = sscanf(pb, "%%%%[ pagecount: %d ]%%%%\r", &pc2); 746 } 747 if ((pb == NULL) || (sc != 1)) { 748 fprintf(stderr, "%s: accounting error 2 (%s)\n", prog,pbuf); 749 VOIDC fflush(stderr); 750 } 751 else if ((pc2 < pc1) || (pc1 < 0) || (pc2 < 0)) { 752 fprintf(stderr,"%s: accounting error 3 %d %d\n", prog,pc1,pc2); 753 VOIDC fflush(stderr); 754 } 755 else if (freopen(accountingfile, "a", stdout) != NULL) { 756 printf("%7.2f\t%s:%s\n", (float)(pc2 - pc1), host, name); 757 VOIDC fclose(stdout); 758 } 759 } 760 761 /* all done -- let sender know */ 762 if (havejobout) VOIDC fclose(jobout); 763 VOIDC fclose(psin); 764 exit(0); /* to parent */ 765 } 766 } 767 768 /* send the file ".banner" */ 769 private SendBanner() 770 { 771 register int banner; 772 int cnt; 773 char buf[BUFSIZ]; 774 775 if ((banner = open(".banner",O_RDONLY|O_NDELAY,0)) < 0) return; 776 while ((cnt = read(banner,buf,sizeof buf)) > 0) { 777 VOIDC write(fdsend,buf,cnt); 778 } 779 VOIDC close(banner); 780 } 781 782 /* search backwards from p in start for patt */ 783 private char *FindPattern(p, start, patt) 784 char *p; 785 char *start; 786 char *patt; 787 { 788 int patlen; 789 patlen = strlen(patt); 790 791 p -= patlen; 792 for (; p >= start; p--) { 793 if (strncmp(p, patt, patlen) == 0) return(p); 794 } 795 return ((char *)NULL); 796 } 797 798 private GotChar(c) 799 register int c; 800 { 801 static char linebuf[BUFSIZ]; 802 static char *cp = linebuf; 803 static enum State {normal, onep, twop, inmessage, 804 close1, close2, close3, close4} st = normal; 805 char *match, *last; 806 807 switch (st) { 808 case normal: 809 if (c == '%') { 810 st = onep; 811 cp = linebuf; 812 *cp++ = c; 813 break; 814 } 815 putc(c,jobout); 816 VOIDC fflush(jobout); 817 break; 818 case onep: 819 if (c == '%') { 820 st = twop; 821 *cp++ = c; 822 break; 823 } 824 putc('%',jobout); 825 putc(c,jobout); 826 VOIDC fflush(jobout); 827 st = normal; 828 break; 829 case twop: 830 if (c == '\[') { 831 st = inmessage; 832 *cp++ = c; 833 break; 834 } 835 if (c == '\%') { 836 putc('%',jobout); 837 VOIDC fflush(jobout); 838 /* don't do anything to cp */ 839 break; 840 } 841 putc('%',jobout); 842 putc('%',jobout); 843 VOIDC fflush(jobout); 844 st = normal; 845 break; 846 case inmessage: 847 *cp++ = c; 848 if (c == '\]') st = close1; 849 break; 850 case close1: 851 *cp++ = c; 852 switch (c) { 853 case '%': st = close2; break; 854 case '\]': st = close1; break; 855 default: st = inmessage; break; 856 } 857 break; 858 case close2: 859 *cp++ = c; 860 switch (c) { 861 case '%': st = close3; break; 862 case '\]': st = close1; break; 863 default: st = inmessage; break; 864 } 865 break; 866 case close3: 867 *cp++ = c; 868 switch (c) { 869 case '\r': st = close4; break; 870 case '\]': st = close1; break; 871 default: st = inmessage; break; 872 } 873 break; 874 case close4: 875 *cp++ = c; 876 switch(c) { 877 case '\n': st = normal; break; 878 case '\]': st = close1; break; 879 default: st = inmessage; break; 880 } 881 if (st == normal) { 882 /* parse complete message */ 883 last = cp; 884 *cp = 0; 885 debugp((stderr,">>%s",linebuf)); 886 if (match = FindPattern(cp, linebuf, " PrinterError: ")) { 887 if (*(match-1) != ':') { 888 fprintf(stderr,"%s",linebuf); 889 VOIDC fflush(stderr); 890 *(last-6) = 0; 891 Status(match+15); 892 } 893 else { 894 last = index(match,';'); 895 *last = 0; 896 Status(match+15); 897 } 898 } 899 else if (match = FindPattern(cp, linebuf, " status: ")) { 900 match += 9; 901 if (strncmp(match,"idle",4) == 0) { 902 /* we are hopelessly lost, get everyone to quit */ 903 fprintf(stderr,"%s: ERROR: printer is idle, giving up!\n",prog); 904 VOIDC fflush(stderr); 905 VOIDC kill(getppid(),SIGKILL); /* will this work */ 906 exit(THROW_AWAY); 907 } 908 else { 909 /* one of: busy, waiting, printing, initializing */ 910 /* clear status message */ 911 RestoreStatus(); 912 } 913 } 914 else { 915 /* message not for us */ 916 fprintf(jobout,"%s",linebuf); 917 VOIDC fflush(jobout); 918 st = normal; 919 break; 920 } 921 } 922 break; 923 default: 924 fprintf(stderr,"bad case;\n"); 925 } 926 return; 927 } 928 929 /* backup "status" message file in ".status", 930 * in case there is a PrinterError 931 */ 932 933 private BackupStatus(file1, file2) 934 char *file1, *file2; 935 { 936 register int fd1, fd2; 937 char buf[BUFSIZ]; 938 int cnt; 939 940 VOIDC umask(0); 941 fd1 = open(file1, O_WRONLY|O_CREAT, 0664); 942 if ((fd1 < 0) || (flock(fd1,LOCK_EX) < 0)) { 943 VOIDC unlink(file1); 944 VOIDC flock(fd1,LOCK_UN); 945 VOIDC close(fd1); 946 fd1 = open(file1, O_WRONLY|O_CREAT, 0664); 947 } 948 if ((fd1 < 0) || (flock(fd1,LOCK_EX) <0)) { 949 fprintf(stderr, "%s: writing %s:\n",prog,file1); 950 perror(prog); 951 VOIDC close(fd1); 952 return; 953 } 954 VOIDC ftruncate(fd1,0); 955 if ((fd2 = open(file2, O_RDONLY,0)) < 0) { 956 fprintf(stderr, "%s: error reading %s:\n", prog, file2); 957 perror(prog); 958 VOIDC close(fd1); 959 return; 960 } 961 cnt = read(fd2,buf,BUFSIZ); 962 VOIDC write(fd1,buf,cnt); 963 VOIDC flock(fd1,LOCK_UN); 964 VOIDC close(fd1); 965 VOIDC close(fd2); 966 } 967 968 /* restore the "status" message from the backed-up ".status" copy */ 969 private RestoreStatus() { 970 BackupStatus("status",".status"); 971 } 972 973 /* report PrinterError via "status" message file */ 974 private Status(msg) 975 register char *msg; 976 { 977 register int fd; 978 char msgbuf[100]; 979 980 if ((fd = open("status",O_WRONLY|O_CREAT,0664)) < 0) return; 981 VOIDC ftruncate(fd,0); 982 sprintf(msgbuf,"Printer Error: may need attention! (%s)\n\0",msg); 983 VOIDC write(fd,msgbuf,strlen(msgbuf)); 984 VOIDC close(fd); 985 } 986 987 /* sending phase alarm handler for sender */ 988 989 private VOID salarm() { 990 991 debugp((stderr,"%s: AS %d %d %d\n",prog,oldprogress,progress,getstatus)); 992 993 /* if progress != oldprogress, we made some progress (sent something) 994 * else, we had two alarms without sending anything... 995 * It may be that a PrinterError has us stopped, or we are computing 996 * for a long time (forever?) -- printer jobtimeout may help here 997 * in any case, all we do is set the flag to get status... 998 * this will help us clear printererror notification 999 */ 1000 1001 oldprogress = progress; 1002 getstatus = TRUE; 1003 1004 /* reset the alarm and return */ 1005 VOIDC alarm(SENDALARM); 1006 return; 1007 } 1008 1009 /* waiting phase alarm handler for sender */ 1010 1011 private VOID walarm() { 1012 static int acount = 0; 1013 1014 debugp((stderr,"%s: WA %d %d %d %d\n", 1015 prog,acount,oldprogress,progress,getstatus)); 1016 1017 if ((oldprogress != progress) || (acount == 4)) { 1018 getstatus = TRUE; 1019 acount = 0; 1020 oldprogress = progress; 1021 } 1022 else acount++; 1023 1024 /* reset alarm */ 1025 VOIDC alarm(WAITALARM); 1026 1027 /* return to wait loop */ 1028 longjmp(dwait, 0); 1029 } 1030 1031 /* final phase alarm handler for sender */ 1032 1033 private VOID falarm() { 1034 1035 debugp((stderr,"%s: FA %d %d %d\n",prog,oldprogress,progress,getstatus)); 1036 1037 /* no reason to count progress, just get status */ 1038 if (!intrup) { 1039 VOIDC write(fdsend, statusbuf, 1); 1040 } 1041 getstatus = FALSE; 1042 1043 /* reset alarm */ 1044 VOIDC alarm(WAITALARM); 1045 return; 1046 } 1047 1048 /* initial interrupt handler - before communications begin, so 1049 * nothing to be sent to printer 1050 */ 1051 private VOID intinit() { 1052 long clock; 1053 1054 /* get rid of banner file */ 1055 VOIDC unlink(".banner"); 1056 1057 fprintf(stderr,"%s: abort (during setup)\n",prog); 1058 VOIDC fflush(stderr); 1059 1060 /* these next two may be too cautious */ 1061 VOIDC kill(0,SIGINT); 1062 while (wait((union wait *) 0) > 0); 1063 1064 if (VerboseLog) { 1065 fprintf (stderr, "%s: end - %s", prog, (time(&clock), ctime(&clock))); 1066 VOIDC fflush(stderr); 1067 } 1068 1069 exit(THROW_AWAY); 1070 } 1071 1072 /* interrupt during sending phase to sender process */ 1073 1074 private VOID intsend() { 1075 /* set flag */ 1076 intrup = TRUE; 1077 longjmp(sendint, 0); 1078 } 1079 1080 /* interrupt during waiting phase to sender process */ 1081 1082 private VOID intwait() { 1083 1084 intrup = TRUE; 1085 1086 fprintf(stderr,"%s: abort (waiting)\n",prog); 1087 VOIDC fflush(stderr); 1088 if (ioctl(fdsend, TIOCFLUSH, &flg) || ioctl(fdsend, TIOCSTART, &flg) 1089 || (write(fdsend, abortbuf, 1) != 1)) { 1090 fprintf(stderr, "%s: error in ioctl(fdsend):\n", prog); 1091 perror(prog); 1092 } 1093 1094 /* VOIDC alarm(2); /* force an alarm soon to get us out of wait! ? */ 1095 longjmp(dwait, 0); 1096 } 1097 1098 /* EMT for reverse filter, avoid printer timeout at the expense 1099 * of performance (sigh) 1100 */ 1101 1102 private VOID reverseready() { 1103 revdone = TRUE; 1104 longjmp(waitonreverse, 0); 1105 } 1106 1107 /* EMT on startup to sender -- signalled by listener after first status 1108 * message received 1109 */ 1110 1111 private VOID readynow() { 1112 goahead = TRUE; 1113 longjmp(startstatus, 0); 1114 } 1115 1116 /* EMT on sending phase, hard EOF printer died! */ 1117 private VOID emtdead() { 1118 VOIDC alarm(0); 1119 exit(THROW_AWAY); 1120 } 1121 1122 /* EMT during waiting phase -- listener saw an EOF (^D) from printer */ 1123 1124 private VOID emtdone() { 1125 VOIDC alarm(0); 1126 gotemt = TRUE; 1127 longjmp(dwait, 0); 1128 } 1129