1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 /* 33 * 34 * postio - RS-232 serial interface for PostScript printers 35 * 36 * A simple program that manages input and output for PostScript printers. Much 37 * has been added and changed from early versions of the program, but the basic 38 * philosophy is still the same. Don't send real data until we're certain we've 39 * connected to a PostScript printer that's in the idle state and try to hold 40 * the connection until the job is completely done. It's more work than you 41 * might expect is necessary, but should provide a reasonably reliable spooler 42 * interface that can return error indications to the caller via the program's 43 * exit status. 44 * 45 * I've added code that will let you split the program into separate read/write 46 * processes. Although it's not the default it should be useful if you have a 47 * file that will be returning useful data from the printer. The two process 48 * stuff was laid down on top of the single process code and both methods still 49 * work. The implementation isn't as good as it could be, but didn't require 50 * many changes to the original program (despite the fact that there are now 51 * many differences). 52 * 53 * By default the program still runs as a single process. The -R2 option forces 54 * separate read and write processes after the intial connection is made. If you 55 * want that as the default initialize splitme (below) to TRUE. In addition the 56 * -t option that's used to force stuff not recognized as status reports to 57 * stdout also tries to run as two processes (by setting splitme to TRUE). It 58 * will only work if the required code (ie. resetline() in ifdef.c) has been 59 * implemented for your Unix system. I've only tested the System V code. 60 * 61 * Code needed to support interactive mode has also been added, although again 62 * it's not as efficient as it could be. It depends on the system dependent 63 * procedures resetline() and setupstdin() (file ifdef.c) and for now is only 64 * guaranteed to work on System V. Can be requested using the -i option. 65 * 66 * Quiet mode (-q option) is also new, but was needed for some printers 67 * connected to RADIAN. If you're running in quiet mode no status requests will 68 * be sent to the printer while files are being transmitted (ie. in send()). 69 * 70 * The program expects to receive printer status lines that look like, 71 * 72 * %%[ status: idle; source: serial 25 ]%% 73 * %%[ status: waiting; source: serial 25 ]%% 74 * %%[ status: initializing; source: serial 25 ]%% 75 * %%[ status: busy; source: serial 25 ]%% 76 * %%[ status: printing; source: serial 25 ]%% 77 * %%[ status: PrinterError: out of paper; source: serial 25 ]%% 78 * %%[ status: PrinterError: no paper tray; source: serial 25 ]%% 79 * 80 * although this list isn't complete. Sending a '\024' (control T) character 81 * forces the return of a status report. PostScript errors detected on the 82 * printer result in the immediate transmission of special error messages that 83 * look like, 84 * 85 * %%[ Error: undefined; OffendingCommand: xxx ]%% 86 * %%[ Flushing: rest of job (to end-of-file) will be ignored ]%% 87 * 88 * although we only use the Error and Flushing keywords. Finally conditions, 89 * like being out of paper, result in other messages being sent back from the 90 * printer over the communications line. Typical PrinterError messages look 91 * like, 92 * 93 * %%[ PrinterError: out of paper; source: serial 25 ]%% 94 * %%[ PrinterError: paper jam; source: serial 25 ]%% 95 * 96 * although we only use the PrinterError keyword rather than trying to recognize 97 * all possible printer errors. 98 * 99 * The implications of using one process and only flow controlling data going to 100 * the printer are obvious. Job transmission should be reliable, but there can 101 * be data loss in stuff sent back from the printer. Usually that only caused 102 * problems with jobs designed to run on the printer and return useful data 103 * back over the communications line. If that's the kind of job you're sending 104 * call postio with the -t option. That should force the program to split into 105 * separate read and write processes and everything not bracketed by "%%[ " 106 * and " ]%%" strings goes to stdout. In otherwords the data you're expecting 107 * should be separated from the status stuff that goes to the log file (or 108 * stderr). The -R2 option does almost the same thing (ie. separate read and 109 * write processes), but everything that comes back from the printer goes to 110 * the log file (stderr by default) and you'll have to separate your data from 111 * any printer messages. 112 * 113 * A typical command line might be, 114 * 115 * postio -l /dev/tty01 -b 9600 -L log file1 file2 116 * 117 * where -l selects the line, -b sets the baud rate, and -L selects the printer 118 * log file. Since there's no default line, at least not right now, you'll 119 * always need to use the -l option, and if you don't choose a log file stderr 120 * will be used. If you have a program that will be returning data the command 121 * line might look like, 122 * 123 * postio -t -l/dev/tty01 -b9600 -Llog file >results 124 * 125 * Status stuff goes to file log while the data you're expecting back from the 126 * printer gets put in file results. 127 * 128 */ 129 130 #include <stdio.h> 131 #include <unistd.h> 132 #include <stdarg.h> 133 #include <stdlib.h> 134 #include <ctype.h> 135 #include <fcntl.h> 136 #include <signal.h> 137 #include <sys/types.h> 138 #include <sys/wait.h> 139 #include <errno.h> 140 #include <string.h> 141 #include <sys/ioccom.h> 142 #include <sys/ioctl.h> 143 #include <sys/bpp_io.h> 144 #include <sys/ecppsys.h> 145 146 #include "ifdef.h" /* conditional compilation stuff */ 147 #include "gen.h" /* general purpose definitions */ 148 #include "postio.h" /* some special definitions */ 149 150 static char **argv; /* global so everyone can use them */ 151 static int argc; 152 static char *prog_name = ""; /* really just for error messages */ 153 static int x_stat = 0; /* program exit status */ 154 static int debug = OFF; /* debug flag */ 155 static int ignore = OFF; /* what's done for FATAL errors */ 156 static Baud baudtable[] = BAUDTABLE; /* converts strings to termio values */ 157 static int quiet = FALSE; /* no status queries in send if TRUE */ 158 char *postbegin = POSTBEGIN; /* preceeds all the input files */ 159 static int useslowsend = FALSE; /* not recommended! */ 160 static int splitme = FALSE; /* into READ & WRITE procs if TRUE */ 161 static int whatami = READWRITE; /* a READ or WRITE process - or both */ 162 static int otherpid = -1; /* who gets signals if greater than 1 */ 163 static int joinsig = SIGTRAP; /* reader gets when writing is done */ 164 static int writedone = FALSE; /* and then sets this to TRUE */ 165 static char sbuf[MESGSIZE]; /* for parsing the message */ 166 static char *mesgptr = NULL; /* printer msg starts here in mesg[] */ 167 static Status status[] = STATUS; /* for converting status strings */ 168 static int nostatus = NOSTATUS; /* default getstatus() return value */ 169 static int tostdout = FALSE; /* non-status stuff goes to stdout? */ 170 static int currentstate = NOTCONNECTED; /* START, SEND, or DONE */ 171 172 char *line = NULL; /* printer is on this tty line */ 173 short baudrate = BAUDRATE; /* and running at this baud rate */ 174 int stopbits = 1; /* number of stop bits */ 175 int interactive = FALSE; /* interactive mode */ 176 char *block = NULL; /* input file buffer */ 177 int blocksize = BLOCKSIZE; /* and its size in bytes */ 178 int head = 0; /* block[head] is the next character */ 179 int tail = 0; /* one past the last byte in block[] */ 180 int canread = TRUE; /* allow reads */ 181 int canwrite = TRUE; /* and writes if TRUE */ 182 char mesg[MESGSIZE]; /* exactly what came back on ttyi */ 183 char *endmesg = NULL; /* end for readline() in mesg[] */ 184 int ttyi = 0; /* input */ 185 int ttyo = 2; /* and output file descriptors */ 186 FILE *fp_log = stderr; /* log file for stuff from printer */ 187 188 static void init_signals(void); 189 static void interrupt(int); 190 static void options(void); 191 static void initialize(void); 192 static void initialize_parallel(void); 193 static void start(void); 194 static void split(void); 195 static void arguments(void); 196 static void send(int, char *); 197 static void done(void); 198 static void cleanup(void); 199 static void clearline(void); 200 void logit(char *, ...); 201 static void quit(int sig); 202 static void Rest(int t); 203 static int parsemesg(void); 204 static int sendsignal(int); 205 static int writeblock(void); 206 static int Write(int, char *, int); 207 static short getbaud(char *); 208 static char *find(char *, char *); 209 210 void error(int, char *, ...); 211 int getstatus(int); 212 int readblock(int); 213 214 215 /* from parallel.c for parallel interfaces */ 216 extern int is_a_parallel_bpp(int); 217 extern int bpp_state(int); 218 extern int is_a_prnio(int); 219 extern int prnio_state(int); 220 extern int parallel_comm(int, int()); /* arg is bpp_state */ 221 222 /* from ifdef.c for serial interfaces */ 223 extern void setupline(void); 224 extern void setupstdin(int); 225 extern void slowsend(int); 226 extern int resetline(void); 227 extern int readline(void); 228 229 /* 230 * A simple program that manages input and output for PostScript printers. 231 * Can run as a single process or as separate read/write processes. What's 232 * done depends on the value assigned to splitme when split() is called. 233 */ 234 235 int nop(int fd) { return(0); } 236 237 238 int 239 main(int agc, char *agv[]) 240 { 241 argc = agc; 242 argv = agv; 243 prog_name = argv[0]; /* really just for error messages */ 244 245 /* is this a serial or parallel port? */ 246 247 init_signals(); /* sets up interrupt handling */ 248 options(); /* get command line options */ 249 250 setbuf(stderr, NULL); /* unbuffer io for stderr */ 251 252 253 if (line) { 254 close(1); 255 open(line, O_RDWR); 256 257 } 258 259 if (is_a_prnio(1)) { 260 initialize_parallel(); 261 x_stat = parallel_comm(1, prnio_state); 262 } else if (is_a_parallel_bpp(1) || 263 (get_ecpp_status(1) == ECPP_CENTRONICS)) { 264 initialize_parallel(); 265 x_stat = parallel_comm(1, bpp_state); 266 } else if (isatty(1)) { 267 initialize(); /* must be done after options() */ 268 start(); /* make sure the printer is ready */ 269 split(); /* into read/write processes - maybe */ 270 arguments(); /* then send each input file */ 271 done(); /* wait until the printer is finished */ 272 cleanup(); /* make sure the write process stops */ 273 } else { 274 initialize_parallel(); 275 x_stat = parallel_comm(1, nop); 276 } 277 278 279 return (x_stat); /* everything probably went OK */ 280 } 281 282 283 /* 284 * Makes sure we handle interrupts. The proper way to kill the program, if 285 * necessary, is to do a kill -15. That forces a call to interrupt(), which in 286 * turn tries to reset the printer and then exits with a non-zero status. If the 287 * program is running as two processes, sending SIGTERM to either the parent or 288 * child should clean things up. 289 */ 290 291 static void 292 init_signals(void) 293 { 294 if (signal(SIGINT, interrupt) == SIG_IGN) { 295 signal(SIGINT, SIG_IGN); 296 signal(SIGQUIT, SIG_IGN); 297 signal(SIGHUP, SIG_IGN); 298 } else { 299 signal(SIGHUP, interrupt); 300 signal(SIGQUIT, interrupt); 301 } 302 303 signal(SIGTERM, interrupt); 304 305 } 306 307 308 /* 309 * Reads and processes the command line options. The -R2, -t, and -i options all 310 * force separate read and write processes by eventually setting splitme to TRUE 311 * (check initialize()). The -S option is not recommended and should only be 312 * used as a last resort! 313 */ 314 315 static void 316 options(void) 317 { 318 int ch; /* return value from getopt() */ 319 char *optnames = "b:il:qs:tB:L:P:R:SDI"; 320 321 extern char *optarg; /* used by getopt() */ 322 extern int optind; 323 324 while ((ch = getopt(argc, argv, optnames)) != EOF) { 325 326 switch (ch) { 327 328 case 'b': /* baud rate string */ 329 baudrate = getbaud(optarg); 330 break; 331 332 case 'i': /* interactive mode */ 333 interactive = TRUE; 334 break; 335 336 case 'l': /* printer line */ 337 line = optarg; 338 break; 339 340 case 'q': /* no status queries - for RADIAN? */ 341 quiet = TRUE; 342 break; 343 344 case 's': /* use 2 stop bits - for UNISON? */ 345 if ((stopbits = atoi(optarg)) < 1 || stopbits > 2) 346 stopbits = 1; 347 break; 348 349 case 't': /* non-status stuff goes to stdout */ 350 tostdout = TRUE; 351 break; 352 353 case 'B': /* set the job buffer size */ 354 if ((blocksize = atoi(optarg)) <= 0) 355 blocksize = BLOCKSIZE; 356 break; 357 358 case 'L': /* printer log file */ 359 if ((fp_log = fopen(optarg, "w")) == NULL) { 360 fp_log = stderr; 361 error(NON_FATAL, "can't open log file %s", optarg); 362 } /* End if */ 363 break; 364 365 case 'P': /* initial PostScript code */ 366 postbegin = optarg; 367 break; 368 369 case 'R': /* run as one or two processes */ 370 if (atoi(optarg) == 2) 371 splitme = TRUE; 372 else splitme = FALSE; 373 break; 374 375 case 'S': /* slow and kludged up vers. of send */ 376 useslowsend = TRUE; 377 break; 378 379 case 'D': /* debug flag */ 380 debug = ON; 381 break; 382 383 case 'I': /* ignore FATAL errors */ 384 ignore = ON; 385 break; 386 387 case '?': /* don't understand the option */ 388 error(FATAL, ""); 389 break; 390 391 default: /* don't know what to do for ch */ 392 error(FATAL, "missing case for option %c\n", ch); 393 break; 394 395 } /* End switch */ 396 397 } /* End while */ 398 399 argc -= optind; /* get ready for non-option args */ 400 argv += optind; 401 402 } 403 404 405 /* 406 * Called from options() to convert a baud rate string into an appropriate 407 * termio value. *rate is looked up in baudtable[] and if it's found, the 408 * corresponding value is returned to the caller. 409 */ 410 411 static short 412 getbaud(char *rate) /* string representing the baud rate */ 413 { 414 int i; /* for looking through baudtable[] */ 415 416 for (i = 0; baudtable[i].rate != NULL; i++) 417 if (strcmp(rate, baudtable[i].rate) == 0) 418 return (baudtable[i].val); 419 420 error(FATAL, "don't recognize baud rate %s", rate); 421 /*NOTREACHED*/ 422 return (0); 423 424 } 425 426 427 /* 428 * Initialization, a few checks, and a call to setupline() (file ifdef.c) to 429 * open and configure the communications line. Settings for interactive mode 430 * always take precedence. The setupstdin() call with an argument of 0 saves 431 * the current terminal settings if interactive mode has been requested - 432 * otherwise nothing's done. Unbuffering stdout (via the setbuf() call) isn't 433 * really needed on System V since it's flushed whenever terminal input is 434 * requested. It's more efficient if we buffer the stdout (on System V) but 435 * safer (for other versions of Unix) if we include the setbuf() call. 436 */ 437 438 static void 439 initialize(void) 440 { 441 whatami = READWRITE; /* always run start() as one process */ 442 canread = canwrite = TRUE; 443 444 if (line == NULL) /* kludge for lp - they use -t option */ 445 tostdout = FALSE; 446 447 if (tostdout == TRUE) /* force separate read/write procs */ 448 splitme = TRUE; 449 450 if (interactive == TRUE) { /* interactive mode settings win */ 451 quiet = FALSE; 452 tostdout = FALSE; 453 splitme = TRUE; 454 blocksize = 1; 455 postbegin = NULL; 456 useslowsend = FALSE; 457 nostatus = INTERACTIVE; 458 setbuf(stdout, NULL); 459 } 460 461 if (useslowsend == TRUE) { /* last resort only - not recommended */ 462 quiet = FALSE; 463 splitme = FALSE; 464 if (blocksize > 1024) /* don't send too much all at once */ 465 blocksize = 1024; 466 } 467 468 if (line == NULL && (interactive == TRUE || tostdout == TRUE)) 469 error(FATAL, "a printer line must be supplied - use the -l option"); 470 471 if ((block = malloc(blocksize)) == NULL) 472 error(FATAL, "no memory"); 473 474 endmesg = mesg + sizeof mesg - 2; /* one byte from last pos. in mesg */ 475 476 setupline(); /* configure the communications line */ 477 setupstdin(0); /* save current stdin term settings */ 478 479 } 480 481 static void 482 initialize_parallel(void) 483 { 484 if ((block = malloc(blocksize)) == NULL) 485 error(FATAL, "no memory"); 486 } 487 488 489 /* 490 * Tries to put the printer in the IDLE state before anything important is sent. 491 * Run as a single process no matter what has been assigned to splitme. Separate 492 * read and write processes, if requested, will be created after we're done 493 * here. 494 */ 495 496 static void 497 start(void) 498 { 499 int longwait = 0; 500 501 logit("printer startup\n"); 502 503 currentstate = START; 504 clearline(); 505 506 for (;;) 507 switch (getstatus(1)) { 508 509 case IDLE: 510 case INTERACTIVE: 511 if (postbegin != NULL && *postbegin != '\0') 512 Write(ttyo, postbegin, strlen(postbegin)); 513 clearline(); 514 return; 515 516 case BUSY: 517 Write(ttyo, "\003", 1); 518 Rest(1); 519 break; 520 521 /* 03/24/95 - bob golden 522 * The HP LJ3 starts in waiting mode and needs the EOF to move 523 * from waiting to idle. To see what would happen, code was added 524 * to send the INTR on waiting and later changed to INTR/EOF. 525 * The INTR by itself had no effect. The INTR/EOF put the 526 * the printer in a busy status loop from which the only 527 * recovery was to reset the printer. Until further testing 528 * testing is done, do not send an INTR to a HPLJ3 in waiting 529 * state. WAITING moved to a separate case to eliminate the 530 * INTR write. 531 */ 532 case WAITING: 533 Write(ttyo, "\004", 1); 534 Rest(1); 535 break; 536 537 /* 03/24/95 - bob golden 538 * The HP LJ3 seems to process INTR at later times. All the 539 * longwaits are increaased to reduce the number of INTRs sent. 540 */ 541 case ERROR: 542 case FLUSHING: 543 Write(ttyo, "\004", 1); 544 if (longwait++ == 5) { 545 Write(ttyo, "\003", 1); 546 Rest(5); 547 longwait = 0; 548 } 549 Rest(1); 550 break; 551 552 case PRINTERERROR: 553 Rest(15); 554 break; 555 556 case DISCONNECT: 557 error(FATAL, "Disconnected - printer may be offline"); 558 break; 559 560 /* 03/24/95 - bob golden 561 * The ENDJOB case has been removed. The HP LJ3 echoes all EOFs 562 * sent so the ENDJOB has no real meaning. 563 */ 564 case UNKNOWN: 565 clearline(); 566 break; 567 568 default: 569 Rest(1); 570 break; 571 572 } /* End switch */ 573 574 } /* End of start */ 575 576 577 /* 578 * 579 * If splitme is TRUE we fork a process, make the parent handle reading, and let 580 * the child take care of writing. resetline() (file ifdef.c) contains all the 581 * system dependent code needed to reset the communications line for separate 582 * read and write processes. For now it's expected to return TRUE or FALSE and 583 * that value controls whether we try the fork. I've only tested the two process 584 * stuff for System V. Other versions of resetline() may just be dummy 585 * procedures that always return FALSE. If the fork() failed previous versions 586 * continued as a single process, although the implementation wasn't quite 587 * right, but I've now decided to quit. The main reason is a Datakit channel 588 * may be configured to flow control data in both directions, and if we run 589 * postio over that channel as a single process we likely will end up in 590 * deadlock. 591 */ 592 593 static void 594 split(void) 595 { 596 int pid; 597 598 if (splitme == TRUE) 599 if (resetline() == TRUE) { 600 pid = getpid(); 601 signal(joinsig, interrupt); 602 if ((otherpid = fork()) == -1) 603 error(FATAL, "can't fork"); 604 else if (otherpid == 0) { 605 whatami = WRITE; 606 nostatus = WRITEPROCESS; 607 otherpid = pid; 608 setupstdin(1); 609 } else 610 whatami = READ; 611 } else if (interactive == TRUE || tostdout == TRUE) 612 error(FATAL, 613 "can't create two process - check resetline()"); 614 else 615 error(NON_FATAL, 616 "running as a single process - check resetline()"); 617 618 canread = (whatami & READ) ? TRUE : FALSE; 619 canwrite = (whatami & WRITE) ? TRUE : FALSE; 620 } 621 622 623 /* 624 * Makes sure all the non-option command line arguments are processed. If there 625 * aren't any arguments left when we get here we'll send stdin. Input files are 626 * only read and sent to the printer if canwrite is TRUE. Checking it here means 627 * we won't have to do it in send(). If interactive mode is TRUE we'll stay here 628 * forever sending stdin when we run out of files - exit with a break. Actually 629 * the loop is bogus and used at most once when we're in interactive mode 630 * because stdin is in a pseudo raw mode and the read() in readblock() should 631 * never see the end of file. 632 */ 633 634 static void 635 arguments(void) 636 { 637 int fd_in; /* next input file */ 638 639 if (canwrite == TRUE) 640 do /* loop is for interactive mode */ 641 if (argc < 1) 642 send(fileno(stdin), "pipe.end"); 643 else { 644 while (argc > 0) { 645 if ((fd_in = open(*argv, O_RDONLY)) == -1) 646 error(FATAL, "can't open %s", *argv); 647 send(fd_in, *argv); 648 close(fd_in); 649 argc--; 650 argv++; 651 } 652 } 653 while (interactive == TRUE); 654 } 655 656 /* 657 * Sends file *name to the printer. There's nothing left here that depends on 658 * sending and receiving status reports, although it can be reassuring to know 659 * the printer is responding and processing our job. Only the writer gets here 660 * in the two process implementation, and in that case split() has reset 661 * nostatus to WRITEPROCESS and that's what getstatus() always returns. For 662 * now we accept the IDLE state and ENDOFJOB as legitimate and ignore the 663 * INITIALIZING state. 664 * 665 * fd_in next input file 666 * name it's pathname 667 */ 668 669 static void 670 send(int fd_in, char *name) 671 { 672 if (interactive == FALSE) 673 logit("sending file %s\n", name); 674 675 currentstate = SEND; 676 677 if (useslowsend == TRUE) { 678 slowsend(fd_in); 679 return; 680 } 681 682 while (readblock(fd_in)) 683 684 switch (getstatus(0)) { 685 686 case IDLE: 687 case BUSY: 688 case WAITING: 689 case PRINTING: 690 case ENDOFJOB: 691 case PRINTERERROR: 692 case UNKNOWN: 693 case NOSTATUS: 694 case WRITEPROCESS: 695 case INTERACTIVE: 696 writeblock(); 697 break; 698 699 case ERROR: 700 fprintf(stderr, "%s", mesg); /* for csw */ 701 error(USER_FATAL, "PostScript Error"); 702 break; 703 704 case FLUSHING: 705 error(USER_FATAL, "Flushing Job"); 706 break; 707 708 case DISCONNECT: 709 error(FATAL, "Disconnected - printer may be offline"); 710 break; 711 712 } 713 714 } 715 716 717 /* 718 * Tries to stay connected to the printer until we're reasonably sure the job is 719 * complete. It's the only way we can recover error messages or data generated 720 * by the PostScript program and returned over the communication line. Actually 721 * doing it correctly for all possible PostScript jobs is more difficult that it 722 * might seem. For example if we've sent several jobs, each with their own EOF 723 * mark, then waiting for ENDOFJOB won't guarantee all the jobs have completed. 724 * Even waiting for IDLE isn't good enough. Checking for the WAITING state after 725 * all the files have been sent and then sending an EOF may be the best 726 * approach, but even that won't work all the time - we could miss it or might 727 * not get there. Even sending our own special PostScript job after all the 728 * input files has it's own different set of problems, but probably could work 729 * (perhaps by printing a fake status message or just not timing out). Anyway 730 * it's probably not worth the trouble so for now we'll quit if writedone is 731 * TRUE and we get ENDOFJOB or IDLE. 732 * 733 * If we're running separate read and write processes the reader gets here after 734 * after split() while the writer goes to send() and only gets here after all 735 * the input files have been transmitted. When they're both here the writer 736 * sends the reader signal joinsig and that forces writedone to TRUE in the 737 * reader. At that point the reader can begin looking for an indication of the 738 * end of the job. The writer hangs around until the reader kills it (usually 739 * in cleanup()) sending occasional status requests. 740 */ 741 742 static void 743 done(void) 744 { 745 int sleeptime = 15; /* for 'out of paper' etc. */ 746 int longwait = 0; 747 748 if (canwrite == TRUE) 749 logit("waiting for end of job\n"); 750 751 currentstate = DONE; 752 writedone = (whatami == READWRITE) ? TRUE : FALSE; 753 754 for (;;) { 755 756 switch (getstatus(1)) { 757 case WRITEPROCESS: 758 if (writedone == FALSE) { 759 sendsignal(joinsig); 760 Write(ttyo, "\004", 1); 761 writedone = TRUE; 762 sleeptime = 1; 763 } 764 Rest(sleeptime++); 765 break; 766 767 /* 03/24/95 - bob golden 768 * For the HP LJ3 INTR sent while in the waiting state have 769 * either had no effect or put the printer into a unrecoverable 770 * loop. Further testing may reveal this to not be the case 771 * but for now, remove the send INTR. 772 */ 773 case WAITING: 774 Write(ttyo, "\004", 1); 775 Rest(1); 776 sleeptime = 15; 777 break; 778 779 /* 03/24/95 - bob golden 780 * ENDOFJOB case removed here. The HP LJ 3 echoes all EOFs sent so 781 * the ENDOFJOB case is meaningless. 782 */ 783 case IDLE: 784 if (writedone == TRUE) { 785 logit("job complete\n"); 786 return; 787 } 788 break; 789 790 /* 03/24/95 - bob golden 791 * During print data transmission, the HP LJ3 stays in 792 * status busy. So give it a rest. 793 * 794 */ 795 case BUSY: 796 case PRINTING: 797 Rest(1); 798 sleeptime = 15; 799 break; 800 801 case INTERACTIVE: 802 Write(ttyo, "\004", 1); 803 sleeptime = 15; 804 break; 805 806 case PRINTERERROR: 807 Rest(sleeptime++); 808 break; 809 810 case ERROR: 811 Write(ttyo, "\004", 1); 812 fprintf(stderr, "%s", mesg); /* for csw */ 813 error(USER_FATAL, "PostScript Error"); 814 return; 815 816 case FLUSHING: 817 Write(ttyo, "\004", 1); 818 error(USER_FATAL, "Flushing Job"); 819 return; 820 821 case DISCONNECT: 822 error(FATAL, "Disconnected - printer may be offline"); 823 return; 824 825 /* 03/24/95 - bob golden 826 * These cases are ignored without a EOF being sent 827 */ 828 case ENDOFJOB: 829 case NOSTATUS: 830 Rest(1); 831 break; 832 833 default: 834 Write(ttyo, "\004", 1); 835 Rest(1); 836 break; 837 838 } 839 840 if (sleeptime > 60) 841 sleeptime = 60; 842 843 } 844 845 } 846 847 848 /* 849 * Only needed if we're running separate read and write processes. Makes sure 850 * the write process is killed after the read process has successfully finished 851 * with all the jobs. sendsignal() returns a -1 if there's nobody to signal so 852 * things work when we're running a single process. 853 */ 854 855 static void 856 cleanup(void) 857 { 858 int w; 859 860 while (sendsignal(SIGKILL) != -1 && (w = wait((int *)0)) != otherpid && 861 w != -1); 862 if ( currentstate != NOTCONNECTED ) 863 Write(ttyo, "\004", 1); 864 } 865 866 867 /* 868 * Fills the input buffer with the next block, provided we're all done with the 869 * last one. Blocks from fd_in are stored in array block[]. head is the index 870 * of the next byte in block[] that's supposed to go to the printer. tail points 871 * one past the last byte in the current block. head is adjusted in writeblock() 872 * after each successful write, while head and tail are reset here each time 873 * a new block is read. Returns the number of bytes left in the current block. 874 * Read errors cause the program to abort. The fake status message that's put 875 * out in quiet mode is only so you can look at the log file and know 876 * something's happening - take it out if you want. 877 */ 878 879 int 880 readblock(int fd_in) 881 { 882 static long blocknum = 1; 883 884 if (head >= tail) { /* done with the last block */ 885 if ((tail = read(fd_in, block, blocksize)) == -1) 886 error(FATAL, "error reading input file"); 887 if (quiet == TRUE && tail > 0) /* put out a fake message? */ 888 logit("%%%%[ status: busy; block: %d ]%%%%\n", blocknum++); 889 head = 0; 890 } 891 892 return (tail - head); 893 894 } 895 896 897 /* 898 * Called from send() when it's OK to send the next block to the printer. head 899 * is adjusted after the write, and the number of bytes that were successfully 900 * written is returned to the caller. 901 */ 902 903 static int 904 writeblock(void) 905 { 906 int count; /* bytes successfully written */ 907 908 if ((count = write(ttyo, &block[head], tail - head)) == -1) 909 error(FATAL, "error writing to %s", line); 910 else if (count == 0) 911 error(FATAL, "printer appears to be offline"); 912 913 head += count; 914 return (count); 915 916 } 917 918 919 /* 920 * Looks for things coming back from the printer on the communications line, 921 * parses complete lines retrieved by readline(), and returns an integer 922 * representation of the current printer status to the caller. If nothing was 923 * available a status request (control T) is sent to the printer and nostatus 924 * is returned to the caller (provided quiet isn't TRUE). Interactive mode 925 * either never returns from readline() or returns FALSE. 926 */ 927 928 int 929 getstatus(int t) /* sleep time after sending '\024' */ 930 { 931 int state = nostatus; /* the current state */ 932 static int laststate = NOSTATUS; /* last state recognized */ 933 934 935 if (canread == TRUE && readline() == TRUE) { 936 state = parsemesg(); 937 if (state != laststate || mesgptr != mesg || debug == ON) 938 logit("%s", mesg); 939 940 if (tostdout == TRUE && currentstate != START) { 941 *mesgptr = '\0'; 942 fprintf(stdout, "%s", mesg); 943 } 944 return (laststate = state); 945 } 946 947 if ((quiet == FALSE || currentstate != SEND) && interactive == FALSE) { 948 if (Write(ttyo, "\024", 1) != 1) 949 error(FATAL, "printer appears to be offline"); 950 if (t > 0) Rest(t); 951 } 952 953 return (nostatus); 954 } 955 956 957 /* 958 * 959 * Parsing the lines that readline() stores in mesg[] is messy, and what's done 960 * here isn't completely correct nor as fast as it could be. The general format 961 * of lines that come back from the printer (assuming no data loss) is: 962 * 963 * str%%[ key: val; key: val; key: val ]%%\n 964 * 965 * where str can be most anything not containing a newline and printer reports 966 * (eg. status or error messages) are bracketed by "%%[ " and " ]%%" strings and 967 * end with a newline. Usually we'll have the string or printer report but not 968 * both. For most jobs the leading string will be empty, but could be anything 969 * generated on a printer and returned over the communications line using the 970 * PostScript print operator. I'll assume PostScript jobs are well behaved and 971 * never bracket their messages with "%%[ " and " ]%%" strings that delimit 972 * status or error messages. 973 * 974 * Printer reports consist of one or more key/val pairs, and what we're 975 * interested in (status or error indications) may not be the first pair in the 976 * list. In addition we'll sometimes want the value associated with a keyword 977 * (eg. when key = status) and other times we'll want the keyword (eg. when 978 * key = Error or Flushing). The last pair isn't terminated by a semicolon and 979 * a value string often contains many space separated words and it can even 980 * include colons in meaningful places. I've also decided to continue 981 * converting things to lower case before doing the lookup in status[]. The 982 * isupper() test is for Berkeley systems. 983 */ 984 985 static int 986 parsemesg(void) 987 { 988 char *e; /* end of printer message in mesg[] */ 989 char *key, *val; /* keyword/value strings in sbuf[] */ 990 char *p; /* for converting to lower case etc. */ 991 int i; /* where *key was found in status[] */ 992 993 if (*(mesgptr = find("%%[ ", mesg)) != '\0' && 994 *(e = find(" ]%%", mesgptr+4)) != '\0') { 995 996 strcpy(sbuf, mesgptr+4); /* don't change mesg[] */ 997 sbuf[e-mesgptr-4] = '\0'; /* ignore the trailing " ]%%" */ 998 999 for (key = strtok(sbuf, " :"); key != NULL; 1000 key = strtok(NULL, " :")) { 1001 if ((val = strtok(NULL, ";")) != NULL && 1002 strcmp(key, "status") == 0) 1003 key = val; 1004 1005 for (; *key == ' '; key++); /* skip leading space */ 1006 for (p = key; *p; p++) /* conv to lower case */ 1007 if (*p == ':' || *p == ',') { 1008 *p = '\0'; 1009 break; 1010 } else if (isupper(*p)) 1011 *p = tolower(*p); 1012 1013 for (i = 0; status[i].state != NULL; i++) 1014 if (strcmp(status[i].state, key) == 0) 1015 return (status[i].val); 1016 } 1017 } else if (strcmp(mesg, "CONVERSATION ENDED.\n") == 0) 1018 return (DISCONNECT); 1019 1020 return (nostatus); 1021 } 1022 1023 1024 /* 1025 * Looks for *str1 in string *str2. Returns a pointer to the start of the 1026 * substring if it's found or to the end of string str2 otherwise. 1027 */ 1028 1029 static char * 1030 find(char *str1, char *str2) 1031 { 1032 char *s1, *s2; /* can't change str1 or str2 too fast */ 1033 1034 for (; *str2 != '\0'; str2++) { 1035 for (s1 = str1, s2 = str2; *s1 != '\0' && *s1 == *s2; s1++, s2++); 1036 if (*s1 == '\0') 1037 break; 1038 } 1039 1040 return (str2); 1041 1042 } 1043 1044 1045 /* 1046 * Reads characters from the input line until nothing's left. Don't do 1047 * anything if we're currently running separate read and write processes. 1048 */ 1049 1050 static void 1051 clearline(void) 1052 { 1053 if (whatami == READWRITE) 1054 while (readline() != FALSE); 1055 1056 } 1057 1058 1059 /* 1060 * Sends signal sig to the other process if we're running as separate read and 1061 * write processes. Returns the result of the kill if there's someone else to 1062 * signal or -1 if we're running alone. 1063 * 1064 */ 1065 1066 static int 1067 sendsignal(int sig) 1068 { 1069 if (whatami != READWRITE && otherpid > 1) 1070 return (kill(otherpid, sig)); 1071 1072 return (-1); 1073 } 1074 1075 1076 /* 1077 * Caught a signal - all except joinsig cause the program to quit. joinsig is 1078 * the signal sent by the writer to the reader after all the jobs have been 1079 * transmitted. Used to tell the read process when it can start looking for 1080 * the end of the job. 1081 */ 1082 1083 static void 1084 interrupt(int sig) 1085 { 1086 signal(sig, SIG_IGN); 1087 1088 if (sig != joinsig) { 1089 x_stat |= FATAL; 1090 if (canread == TRUE) 1091 if (interactive == FALSE) 1092 error(NON_FATAL, "signal %d abort", sig); 1093 else error(NON_FATAL, "quitting"); 1094 quit(sig); 1095 } 1096 1097 writedone = TRUE; 1098 signal(joinsig, interrupt); 1099 } 1100 1101 1102 /* 1103 * Simple routine that's used to write a message to the log file. 1104 */ 1105 1106 void 1107 logit(char *mesg, ...) 1108 { 1109 va_list ap; 1110 1111 va_start(ap, mesg); 1112 vfprintf(fp_log, mesg, ap); 1113 va_end(ap); 1114 1115 fflush(fp_log); 1116 1117 } 1118 1119 /* 1120 * Called when we've run into some kind of program error. First *mesg is 1121 * printed. If kind is FATAL and we're not ignoring errors the program 1122 * will be terminated. If mesg is NULL or *mesg is the NULL string nothing 1123 * will be printed. 1124 */ 1125 1126 void 1127 error(int kind, char *mesg, ...) 1128 { 1129 va_list ap; 1130 1131 if (mesg != NULL && *mesg != '\0') { 1132 fprintf(fp_log, "%s: ", prog_name); 1133 1134 va_start(ap, mesg); 1135 vfprintf(fp_log, mesg, ap); 1136 va_end(ap); 1137 1138 putc('\n', fp_log); 1139 } 1140 1141 x_stat |= kind; 1142 1143 if (kind != NON_FATAL && ignore == OFF) 1144 quit(SIGTERM); 1145 1146 } 1147 1148 1149 /* 1150 * 1151 * Makes sure everything is properly cleaned up if there's a signal or FATAL 1152 * error that should cause the program to terminate. The sleep by the write 1153 * process is to help give the reset sequence a chance to reach the printer 1154 * before we break the connection - primarily for printers connected to Datakit. 1155 * There's a very slight chance the reset sequence that's sent to the printer 1156 * could get us stuck here. Simplest solution is don't bother to send it - 1157 * everything works without it. Flushing ttyo would be better, but means yet 1158 * another system dependent procedure in ifdef.c! I'll leave things be for now. 1159 */ 1160 1161 static void 1162 quit(int sig) 1163 { 1164 int w; 1165 1166 signal(sig, SIG_IGN); 1167 ignore = ON; 1168 1169 while (sendsignal(sig) != -1 && (w = wait((int *)0)) != otherpid && 1170 w != -1); 1171 1172 setupstdin(2); 1173 1174 if (currentstate != NOTCONNECTED) 1175 Write(ttyo, "\003\004", 2); 1176 alarm(0); /* prevents sleep() loop on V9 systems */ 1177 Rest(2); 1178 1179 exit(x_stat); 1180 1181 } 1182 1183 1184 /* 1185 * Used to replace sleep() calls. Only needed if we're running the program as 1186 * a read and write process and don't want to have the read process sleep. Most 1187 * sleeps are in the code because of the non-blocking read used by the single 1188 * process implementation. Probably should be a macro. 1189 */ 1190 1191 static void 1192 Rest(int t) 1193 { 1194 if (t > 0 && canwrite == TRUE) 1195 sleep(t); 1196 1197 } 1198 1199 1200 /* 1201 * Used to replace some of the read() calls. Only needed if we're running 1202 * separate read and write processes. Should only be used to replace read calls 1203 * on ttyi. Always returns 0 to the caller if the process doesn't have its 1204 * READ flag set. Probably should be a macro. 1205 */ 1206 1207 #ifdef NEVER 1208 1209 static int 1210 Read(int fd, char *buf, int n) 1211 { 1212 int count; 1213 1214 if (canread == TRUE) { 1215 if ((count = read(fd, buf, n)) == -1 && errno == EINTR) 1216 count = 0; 1217 } else count = 0; 1218 1219 return (count); 1220 1221 } 1222 1223 #endif /* NEVER */ 1224 1225 1226 /* 1227 * 1228 * Used to replace some of the write() calls. Again only needed if we're running 1229 * separate read and write processes. Should only be used to replace write calls 1230 * on ttyo. Always returns n to the caller if the process doesn't have its WRITE 1231 * flag set. Should also probably be a macro. 1232 * 1233 */ 1234 1235 static int 1236 Write(int fd, char *buf, int n) 1237 { 1238 int count; 1239 1240 if (canwrite == TRUE) { 1241 if ((count = write(fd, buf, n)) == -1 && errno == EINTR) 1242 count = n; 1243 } else count = n; 1244 1245 return (count); 1246 } 1247