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