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