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