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
150 static char **argv; /* global so everyone can use them */
151 static int argc;
152 static char *prog_name = ""; /* really just for error messages */
153 static int x_stat = 0; /* program exit status */
154 static int debug = OFF; /* debug flag */
155 static int ignore = OFF; /* what's done for FATAL errors */
156 static Baud baudtable[] = BAUDTABLE; /* converts strings to termio values */
157 static int quiet = FALSE; /* no status queries in send if TRUE */
158 char *postbegin = POSTBEGIN; /* preceeds all the input files */
159 static int useslowsend = FALSE; /* not recommended! */
160 static int splitme = FALSE; /* into READ & WRITE procs if TRUE */
161 static int whatami = READWRITE; /* a READ or WRITE process - or both */
162 static int otherpid = -1; /* who gets signals if greater than 1 */
163 static int joinsig = SIGTRAP; /* reader gets when writing is done */
164 static int writedone = FALSE; /* and then sets this to TRUE */
165 static char sbuf[MESGSIZE]; /* for parsing the message */
166 static char *mesgptr = NULL; /* printer msg starts here in mesg[] */
167 static Status status[] = STATUS; /* for converting status strings */
168 static int nostatus = NOSTATUS; /* default getstatus() return value */
169 static int tostdout = FALSE; /* non-status stuff goes to stdout? */
170 static int currentstate = NOTCONNECTED; /* START, SEND, or DONE */
171
172 char *line = NULL; /* printer is on this tty line */
173 short baudrate = BAUDRATE; /* and running at this baud rate */
174 int stopbits = 1; /* number of stop bits */
175 int interactive = FALSE; /* interactive mode */
176 char *block = NULL; /* input file buffer */
177 int blocksize = BLOCKSIZE; /* and its size in bytes */
178 int head = 0; /* block[head] is the next character */
179 int tail = 0; /* one past the last byte in block[] */
180 int canread = TRUE; /* allow reads */
181 int canwrite = TRUE; /* and writes if TRUE */
182 char mesg[MESGSIZE]; /* exactly what came back on ttyi */
183 char *endmesg = NULL; /* end for readline() in mesg[] */
184 int ttyi = 0; /* input */
185 int ttyo = 2; /* and output file descriptors */
186 FILE *fp_log = stderr; /* log file for stuff from printer */
187
188 static void init_signals(void);
189 static void interrupt(int);
190 static void options(void);
191 static void initialize(void);
192 static void initialize_parallel(void);
193 static void start(void);
194 static void split(void);
195 static void arguments(void);
196 static void send(int, char *);
197 static void done(void);
198 static void cleanup(void);
199 static void clearline(void);
200 void logit(char *, ...);
201 static void quit(int sig);
202 static void Rest(int t);
203 static int parsemesg(void);
204 static int sendsignal(int);
205 static int writeblock(void);
206 static int Write(int, char *, int);
207 static short getbaud(char *);
208 static char *find(char *, char *);
209
210 void error(int, char *, ...);
211 int getstatus(int);
212 int readblock(int);
213
214
215 /* from parallel.c for parallel interfaces */
216 extern int is_a_parallel_bpp(int);
217 extern int bpp_state(int);
218 extern int is_a_prnio(int);
219 extern int prnio_state(int);
220 extern int parallel_comm(int, int()); /* arg is bpp_state */
221
222 /* from ifdef.c for serial interfaces */
223 extern void setupline(void);
224 extern void setupstdin(int);
225 extern void slowsend(int);
226 extern int resetline(void);
227 extern 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
nop(int fd)235 int nop(int fd) { return(0); }
236
237
238 int
main(int agc,char * agv[])239 main(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
291 static void
init_signals(void)292 init_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
315 static void
options(void)316 options(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
411 static short
getbaud(char * rate)412 getbaud(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
438 static void
initialize(void)439 initialize(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
481 static void
initialize_parallel(void)482 initialize_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
496 static void
start(void)497 start(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
593 static void
split(void)594 split(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
634 static void
arguments(void)635 arguments(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
669 static void
send(int fd_in,char * name)670 send(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
742 static void
done(void)743 done(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
855 static void
cleanup(void)856 cleanup(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
879 int
readblock(int fd_in)880 readblock(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
903 static int
writeblock(void)904 writeblock(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
928 int
getstatus(int t)929 getstatus(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
985 static int
parsemesg(void)986 parsemesg(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
1029 static char *
find(char * str1,char * str2)1030 find(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
1050 static void
clearline(void)1051 clearline(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
1066 static int
sendsignal(int sig)1067 sendsignal(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
1083 static void
interrupt(int sig)1084 interrupt(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
1106 void
logit(char * mesg,...)1107 logit(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
1126 void
error(int kind,char * mesg,...)1127 error(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
1161 static void
quit(int sig)1162 quit(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
1191 static void
Rest(int t)1192 Rest(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
1209 static int
Read(int fd,char * buf,int n)1210 Read(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
1235 static int
Write(int fd,char * buf,int n)1236 Write(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