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