xref: /plan9/sys/src/cmd/postscript/tcpostio/tcpostio.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 #include <stdio.h>
2 #include <stdlib.h>
3 
4 #ifdef plan9
5 #include <bsd.h>
6 #endif
7 
8 #include <string.h>
9 #include <signal.h>
10 #include <errno.h>
11 #include <sys/types.h>
12 #include <sys/time.h>
13 
14 #include <sys/socket.h>
15 
16 extern	int dial_debug;
17 extern	int	dial(char*, char*, char*, int*);
18 
19 
20 /* debug = 0 for no debugging */
21 /* debug = 1 for readprinter debugging */
22 /* debug = 2 for sendprinter debugging */
23 /* debug = 3 for full debugging, its hard to read the messages */
24 
25 int debug = 0;
26 #define READTIMEOUT	300
27 #define	RCVSELTIMEOUT	30
28 #define	SNDSELTIMEOUT	300
29 
30 void
31 rdtmout(void) {
32 	fprintf(stderr, "read timeout occurred, check printer\n");
33 }
34 
35 int
36 getline(int fd, char *buf, int len) {
37 	char *bp, c;
38 	int i = 0, n;
39 
40 	bp = buf;
41 	while (alarm(READTIMEOUT),(n=read(fd, bp, 1)) == 1) {
42 		alarm(0);
43 		if (*bp == '\r') continue;
44 		i += n;
45 
46 		c = *bp++;
47 		if (c == '\n' || c == '\004' || i >= len-1)
48 			break;
49 	}
50 	alarm(0);
51 	if (n < 0)
52 		return(n);
53 	*bp = '\0';
54 	return(i);
55 }
56 
57 typedef struct {
58 	char	*state;			/* printer's current status */
59 	int	val;			/* value returned by getstatus() */
60 } Status;
61 
62 /* printer states */
63 #define	INITIALIZING	0
64 #define	IDLE		1
65 #define	BUSY		2
66 #define	WAITING		3
67 #define	PRINTING	4
68 #define	PRINTERERROR	5
69 #define	ERROR		6
70 #define	FLUSHING	7
71 #define UNKNOWN		8
72 
73 /* protocol requests and program states */
74 #define START	'S'
75 unsigned char Start[] = { START };
76 #define ID_LE		'L'
77 unsigned char Id_le[] = { ID_LE };
78 #define	REQ_STAT	'T'
79 unsigned char Req_stat[] = { REQ_STAT };
80 #define	SEND_DATA	'D'
81 unsigned char Send_data[] = { SEND_DATA };
82 #define	SENT_DATA	'A'
83 unsigned char Sent_data[] = { SENT_DATA };
84 #define	WAIT_FOR_EOJ	'W'
85 unsigned char Wait_for_eoj[] = { WAIT_FOR_EOJ };
86 #define END_OF_JOB	'E'
87 unsigned char End_of_job[] = { END_OF_JOB };
88 #define FATAL_ERROR	'F'
89 unsigned char Fatal_error[] = { FATAL_ERROR };
90 #define	WAIT_FOR_IDLE	'I'
91 unsigned char Wait_for_idle[] = { WAIT_FOR_IDLE };
92 #define	OVER_AND_OUT	'O'
93 unsigned char Over_and_out[] = { OVER_AND_OUT };
94 
95 Status	statuslist[] = {
96 	"initializing", INITIALIZING,
97 	"idle", IDLE,
98 	"busy", BUSY,
99 	"waiting", WAITING,
100 	"printing", PRINTING,
101 	"printererror", PRINTERERROR,
102 	"Error", ERROR,
103 	"flushing", FLUSHING,
104 	NULL, UNKNOWN
105 };
106 
107 
108 /* find returns a pointer to the location of string str2 in string str1,
109  * if it exists.  Otherwise, it points to the end of str1.
110  */
111 char *
112 find(char *str1, char *str2) {
113 	char *s1, *s2;
114 
115 	for (; *str1!='\0'; str1++) {
116 		for (s1=str1,s2=str2; *s2!='\0'&&*s1==*s2; s1++,s2++) ;
117 		if ( *s2 == '\0' )
118 	    		break;
119 	}
120 
121 	return(str1);
122 }
123 
124 #define	MESGSIZE	16384
125 int blocksize = 1920;		/* 19200/10, with 1 sec delay between transfers
126 				 * this keeps the queues from building up.
127 				 */
128 char	mesg[MESGSIZE];			/* exactly what came back on ttyi */
129 
130 int
131 parsmesg(char *buf) {
132 	static char sbuf[MESGSIZE];
133 	char	*s;		/* start of printer messsage in mesg[] */
134 	char	*e;		/* end of printer message in mesg[] */
135 	char	*key, *val;	/* keyword/value strings in sbuf[] */
136 	char	*p;		/* for converting to lower case etc. */
137 	int	i;		/* where *key was found in statuslist[] */
138 
139 	if (*(s=find(buf, "%[ "))!='\0' && *(e=find(s, " ]%"))!='\0') {
140 		strcpy(sbuf, s+3);	/* don't change mesg[] */
141 		sbuf[e-(s+3)] = '\0';	/* ignore the trailing " ]%" */
142 
143 		for (key=strtok(sbuf, " :"); key != NULL; key=strtok(NULL, " :"))  {
144 			if (strcmp(key, "Error") == 0)
145 				return(ERROR);
146 			if ((val=strtok(NULL, ";")) != NULL && strcmp(key, "status") == 0)
147 				key = val;
148 
149 	    		for (; *key == ' '; key++) ;	/* skip any leading spaces */
150 			for (p = key; *p; p++)		/* convert to lower case */
151 				if (*p == ':')  {
152 					*p = '\0';
153 					break;
154 				} else if (isupper(*p)) *p = tolower(*p);
155 
156 			for (i=0; statuslist[i].state != NULL; i++) {
157 				if (strcmp(statuslist[i].state, key) == 0)
158 					return(statuslist[i].val);
159 			}
160 		}
161 	}
162 	return(UNKNOWN);
163 }
164 
165 char buf[MESGSIZE];
166 fd_set readfds, writefds, exceptfds;
167 struct timeval rcvtimeout = { RCVSELTIMEOUT, 0 };
168 struct timeval sndtimeout = { SNDSELTIMEOUT, 0 };
169 
170 int
171 readprinter(int printerfd, int pipefd)
172 {
173 	unsigned char proto;
174 	int progstate = START;
175 	int print_wait_msg = 0;
176 	int tocount = 0;
177 	int c, printstat, lastprintstat, n, nfds;
178 
179 
180 	nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
181 	printstat = 0;
182 	signal(SIGALRM, rdtmout);
183 	do {
184 
185 reselect:
186 		/* ask sending process to request printer status */
187 		if (write(pipefd, Req_stat, 1) != 1) {
188 			fprintf(stderr, "request status failed\n");
189 			progstate = FATAL_ERROR;
190 			continue;
191 		}
192 		FD_ZERO(&readfds);	/* lets be anal */
193 		FD_SET(printerfd, &readfds);
194 		FD_SET(pipefd, &readfds);
195 		FD_ZERO(&exceptfds);
196 		FD_SET(printerfd, &exceptfds);
197 		FD_SET(pipefd, &exceptfds);
198 		n = select(nfds, &readfds, (fd_set *)0, &exceptfds, &rcvtimeout);
199 		if (debug&0x1) fprintf(stderr, "readprinter select returned %d\n", n);
200 		if (n == 0) {
201 			/* a timeout occurred */
202 			if (++tocount > 4) {
203 				fprintf(stderr, "printer appears to be offline.\nHP4m printers may be out of paper.\n");
204 				tocount = 0;
205 			}
206 			goto reselect;
207 		}
208 		if (n > 0 && FD_ISSET(printerfd, &exceptfds)) {
209 			/* printer problem */
210 			fprintf(stderr, "printer exception\n");
211 			if (write(pipefd, Fatal_error, 1) != 1) {
212 				fprintf(stderr, "'fatal error' write to pipe failed\n");
213 			}
214 			progstate = FATAL_ERROR;
215 			continue;
216 		}
217 		if (n > 0 && FD_ISSET(pipefd, &exceptfds)) {
218 			/* pipe problem */
219 			fprintf(stderr, "pipe exception\n");
220 			progstate = FATAL_ERROR;
221 			continue;
222 		}
223 		if (n > 0 && FD_ISSET(pipefd, &readfds)) {
224 			/* protocol pipe wants to be read */
225 			if (debug&0x1) fprintf(stderr, "pipe wants to be read\n");
226 			if (read(pipefd, &proto, 1) != 1) {
227 				fprintf(stderr, "read protocol pipe failed\n");
228 				progstate = FATAL_ERROR;
229 				continue;
230 			}
231 			if (debug&0x1) fprintf(stderr, "readprinter: proto=%c\n", proto);
232 			/* change state? */
233 			switch (proto) {
234 			case SENT_DATA:
235 				break;
236 			case WAIT_FOR_EOJ:
237 				if (!print_wait_msg) {
238 					print_wait_msg = 1;
239 					fprintf(stderr, "waiting for end of job\n");
240 				}
241 				progstate = proto;
242 				break;
243 			default:
244 				fprintf(stderr, "received unknown protocol request <%c> from sendfile\n", proto);
245 				break;
246 			}
247 			n--;
248 		}
249 		if (n > 0 && FD_ISSET(printerfd, &readfds)) {
250 			/* printer wants to be read */
251 			if (debug&0x1) fprintf(stderr, "printer wants to be read\n");
252 			if ((c=getline(printerfd, buf, MESGSIZE)) < 0) {
253 				fprintf(stderr, "read printer failed\n");
254 				progstate = FATAL_ERROR;
255 				continue;
256 			}
257 			if (debug&0x1) fprintf(stderr, "%s\n", buf);
258 			if (c==1 && *buf == '\004') {
259 				if (progstate == WAIT_FOR_EOJ) {
260 					if (debug&0x1) fprintf(stderr, "progstate=%c, ", progstate);
261 					fprintf(stderr, "%%[ status: endofjob ]%%\n");
262 /*					progstate = WAIT_FOR_IDLE; */
263 					progstate = OVER_AND_OUT;
264 					if (write(pipefd, Over_and_out, 1) != 1) {
265 						fprintf(stderr, "'fatal error' write to pipe failed\n");
266 					}
267 					continue;
268 				} else {
269 					if (printstat == ERROR) {
270 						progstate = FATAL_ERROR;
271 						continue;
272 					}
273 					if (progstate != START && progstate != WAIT_FOR_IDLE)
274 						fprintf(stderr, "warning: EOF received; program status is '%c'\n", progstate);
275 
276 				}
277 				continue;
278 			}
279 
280 			/* figure out if it was a status line */
281 			lastprintstat = printstat;
282 			printstat = parsmesg(buf);
283 			if (printstat == UNKNOWN || printstat == ERROR
284 			    || lastprintstat != printstat) {
285 				/* print whatever it is that was read */
286 				fprintf(stderr, buf);
287 				fflush(stderr);
288 				if (printstat == UNKNOWN) {
289 					printstat = lastprintstat;
290 					continue;
291 				}
292 			}
293 			switch (printstat) {
294 			case UNKNOWN:
295 				continue;	/* shouldn't get here */
296 			case FLUSHING:
297 			case ERROR:
298 				progstate = FATAL_ERROR;
299 				/* ask sending process to die */
300 				if (write(pipefd, Fatal_error, 1) != 1) {
301 					fprintf(stderr, "Fatal_error mesg write to pipe failed\n");
302 				}
303 				continue;
304 			case INITIALIZING:
305 			case PRINTERERROR:
306 				sleep(1);
307 				break;
308 			case IDLE:
309 				if (progstate == WAIT_FOR_IDLE) {
310 					progstate = OVER_AND_OUT;
311 					if (write(pipefd, Over_and_out, 1) != 1) {
312 						fprintf(stderr, "'fatal error' write to pipe failed\n");
313 					}
314 					continue;
315 				}
316 				progstate = SEND_DATA;
317 
318 				goto dowait;
319 			case BUSY:
320 			case WAITING:
321 			default:
322 				sleep(1);
323 dowait:
324 				switch (progstate) {
325 				case WAIT_FOR_IDLE:
326 				case WAIT_FOR_EOJ:
327 				case START:
328 					sleep(5);
329 					break;
330 
331 				case SEND_DATA:
332 					if (write(pipefd, Send_data, 1) != 1) {
333 						fprintf(stderr, "send data write to pipe failed\n");
334 						progstate = FATAL_ERROR;
335 						continue;
336 					}
337 					break;
338 				default:
339 					fprintf(stderr, "unexpected program state %c\n", progstate);
340 					exit(1);
341 				}
342 				break;
343 			}
344 			n--;
345 		}
346 		if (n > 0) {
347 			fprintf(stderr, "more fds selected than requested!\n");
348 			exit(1);
349 		};
350 	} while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
351 
352 	if (progstate == FATAL_ERROR)
353 		return(1);
354 	else
355 		return(0);
356 }
357 
358 int
359 sendfile(int infd, int printerfd, int pipefd)
360 {
361 	unsigned char proto;
362 	int progstate = START;
363 	int i, n, nfds;
364 	int bytesread,  bytesent = 0;
365 
366 	nfds = ((pipefd>printerfd)?pipefd:printerfd) + 1;
367 
368 	if (write(printerfd, "\004", 1)!=1) {
369 		perror("sendfile:write:");
370 		progstate = FATAL_ERROR;
371 	}
372 	do {
373 		FD_ZERO(&readfds);	/* lets be anal */
374 		FD_SET(pipefd, &readfds);
375 		n = select(nfds, &readfds, (fd_set *)0, (fd_set *)0, &sndtimeout);
376 		if (debug&02) fprintf(stderr, "sendfile select returned %d\n", n);
377 		if (n > 0 && FD_ISSET(pipefd, &readfds)) {
378 			/* protocol pipe wants to be read */
379 			if (read(pipefd, &proto, 1) != 1) {
380 				fprintf(stderr, "read protocol pipe failed\n");
381 				return(1);
382 			}
383 			/* change state? */
384 			if (debug&02) fprintf(stderr, "sendfile command - <%c>\n", proto);
385 			switch (proto) {
386 			case OVER_AND_OUT:
387 			case END_OF_JOB:
388 				progstate = proto;
389 				break;
390 			case SEND_DATA:
391 				bytesread = 0;
392 				do {
393 					i = read(infd, &buf[bytesread], blocksize-bytesread);
394 					if (debug&02) fprintf(stderr, "read %d bytes\n", i);
395 					if (i > 0)
396 						bytesread += i;
397 				} while((i > 0) && (bytesread < blocksize));
398 				if (i < 0) {
399 					fprintf(stderr, "input file read error\n");
400 					progstate = FATAL_ERROR;
401 					break;	/* from switch */
402 				}
403 				if (bytesread > 0) {
404 					if (debug&02) fprintf(stderr, "writing %d bytes\n", bytesread);
405 					if (write(printerfd, buf, bytesread)!=bytesread) {
406 						perror("sendfile:write:");
407 						progstate = FATAL_ERROR;
408 					} else if (write(pipefd, Sent_data, 1)!=1) {
409 						perror("sendfile:write:");
410 						progstate = FATAL_ERROR;
411 					} else {
412 						bytesent += bytesread;
413 					}
414 					fprintf(stderr, "%d sent\n", bytesent);
415 					fflush(stderr);
416 
417 				/* we have reached the end of the input file */
418 				}
419 				if (i == 0) {
420 					if (progstate != WAIT_FOR_EOJ) {
421 						if (write(printerfd, "\004", 1)!=1) {
422 							perror("sendfile:write:");
423 							progstate = FATAL_ERROR;
424 						} else if (write(pipefd, Wait_for_eoj, 1)!=1) {
425 							perror("sendfile:write:");
426 							progstate = FATAL_ERROR;
427 						} else {
428 							progstate = WAIT_FOR_EOJ;
429 						}
430 					}
431 				}
432 				break;
433 			case REQ_STAT:
434 				if (write(printerfd, "\024", 1)!=1) {
435 					fprintf(stderr, "write to printer failed\n");
436 					progstate = FATAL_ERROR;
437 				}
438 				if (debug&02) fprintf(stderr, "^T");
439 				break;
440 			case FATAL_ERROR:
441 				progstate = FATAL_ERROR;
442 			}
443 		} else if (n < 0) {
444 			perror("sendfile:select:");
445 			progstate = FATAL_ERROR;
446 		} else if (n == 0) {
447 			sleep(1);
448 			fprintf(stderr, "sendfile timeout\n");
449 			progstate = FATAL_ERROR;
450 		}
451 	} while ((progstate != FATAL_ERROR) && (progstate != OVER_AND_OUT));
452 	if (write(printerfd, "\004", 1)!=1) {
453 		perror("sendfile:write:");
454 		progstate = FATAL_ERROR;
455 	}
456 
457 	if (debug&02) fprintf(stderr, "%d bytes sent\n", bytesent);
458 	if (progstate == FATAL_ERROR)
459 		return(1);
460 	else
461 		return(0);
462 }
463 
464 void main(int argc, char *argv[]) {
465 	int c, usgflg=0, infd, printerfd;
466 	int cpid;
467 	int pipefd[2];
468 	char *dialstr;
469 	unsigned long rprv, sprv;
470 
471 	dialstr = 0;
472 
473 	while ((c = getopt(argc, argv, "b:d:")) != -1)
474 		switch (c) {
475 		case 'b':
476 			blocksize = atoi(optarg)/10;
477 			if (blocksize > MESGSIZE || blocksize < 1)
478 				blocksize = MESGSIZE;
479 			break;
480 		case 'd':
481 			debug = atoi(optarg);
482 			dial_debug = debug;
483 			break;
484 		case '?':
485 			fprintf(stderr, "unknown option %c\n", c);
486 			usgflg++;
487 		}
488 	if (optind < argc)
489 		dialstr = argv[optind++];
490 	else {
491 		usgflg++;
492 	}
493 	if (usgflg) {
494 		fprintf(stderr, "usage: %s [-b baudrate] net!host!service [infile]\n", argv[0]);
495 		exit (2);
496 	}
497 	if (optind < argc) {
498 		infd = open(argv[optind], 0);
499 		if (infd < 0) {
500 			fprintf(stderr, "cannot open %s\n", argv[optind]);
501 			exit(1);
502 		}
503 		optind++;
504 	} else
505 		infd = 0;
506 
507 	if (debug & 02) fprintf(stderr, "blocksize=%d\n", blocksize);
508 	if (debug) fprintf(stderr, "dialing address=%s\n", dialstr);
509 	printerfd = dial(dialstr, 0, 0, 0);
510 	if (printerfd < 0) exit(1);
511 
512 	fprintf(stderr, "printer startup\n");
513 
514 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipefd) < 0) {
515 		perror("socketpair");
516 		exit(1);
517 	}
518 	switch(cpid = fork()){
519 	case -1:
520 		perror("fork error");
521 		exit(1);
522 	case 0:
523 		close(pipefd[1]);
524 		sprv = sendfile(infd, printerfd, pipefd[0]);	/* child - to printer */
525 		if (debug) fprintf(stderr, "to remote - exiting\n");
526 		exit(sprv);
527 	default:
528 		close(pipefd[0]);
529 		rprv = readprinter(printerfd, pipefd[1]);	/* parent - from printer */
530 		if (debug) fprintf(stderr, "from remote - exiting\n");
531 		while(wait(&sprv) != cpid);
532 		exit(rprv|sprv);
533 	}
534 }
535