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