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