|
|
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.