consistency checking
[unix-history] / usr / src / usr.sbin / lpr / lpd / printjob.c
CommitLineData
5f84f8f0 1#ifndef lint
731697c1 2static char sccsid[] = "@(#)printjob.c 4.15 (Berkeley) %G%";
5f84f8f0
SL
3#endif
4
df7d5679
RC
5/*
6 * printjob -- print jobs in the queue.
7 *
8 * NOTE: the lock file is used to pass information to lpq and lprm.
9 * it does not need to be removed because file locks are dynamic.
10 */
11
12#include "lp.h"
13
54266d1f
RC
14#define DORETURN 0 /* absorb fork error */
15#define DOABORT 1 /* abort if dofork fails */
df7d5679 16
54266d1f
RC
17static char title[80]; /* ``pr'' title */
18static FILE *cfp; /* control file */
19static int pfd; /* printer file descriptor */
20static int ofd; /* output filter file descriptor */
21static int lfd; /* lock file descriptor */
22static int pid; /* pid of lpd process */
23static int prchild; /* id of pr process */
24static int child; /* id of any filters */
25static int ofilter; /* id of output filter, if any */
26static int tof; /* true if at top of form */
845f1693 27static int count; /* Number of files actually printed */
54266d1f 28static int remote; /* true if sending files to remote */
df7d5679 29
845f1693 30static char fromhost[32]; /* user's host machine */
54266d1f 31static char logname[32]; /* user's login name */
731697c1 32static char jobname[100]; /* job or file name */
54266d1f
RC
33static char class[32]; /* classification field */
34static char width[10] = "-w"; /* page width in characters */
35static char length[10] = "-l"; /* page length in lines */
36static char pxwidth[10] = "-x"; /* page width in pixels */
37static char pxlength[10] = "-y"; /* page length in pixels */
38static char indent[10] = "-i0"; /* indentation size in characters */
df7d5679
RC
39
40printjob()
41{
42 struct stat stb;
43 register struct queue *q, **qp;
44 struct queue **queue;
45 register int i, nitems;
46 long pidoff;
47 extern int onintr();
48
df7d5679 49 init(); /* set up capabilities */
8fed920b 50 (void) write(1, "", 1); /* ack that daemon is started */
54266d1f
RC
51 (void) close(1); /* set up log file */
52 (void) close(2);
adec4d9e
SL
53 if (open(LF, O_WRONLY|O_APPEND) < 0)
54 (void) open("/dev/null", O_WRONLY);
54266d1f 55 dup(1);
88f026f7 56 pid = getpid(); /* for use with lprm */
df7d5679 57 setpgrp(0, pid);
f0b73bd5 58 signal(SIGHUP, onintr);
adec4d9e 59 signal(SIGINT, onintr);
f0b73bd5
RC
60 signal(SIGQUIT, onintr);
61 signal(SIGTERM, onintr);
df7d5679
RC
62
63 /*
64 * uses short form file names
65 */
66 if (chdir(SD) < 0) {
67 log("cannot chdir to %s", SD);
68 exit(1);
69 }
88f026f7
RC
70 if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
71 exit(0); /* printing disabled */
845f1693 72 lfd = open(LO, O_WRONLY|O_CREAT, 0644);
c6d1c018
RC
73 if (lfd < 0) {
74 log("cannot create %s", LO);
75 exit(1);
76 }
77 if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
df7d5679
RC
78 if (errno == EWOULDBLOCK) /* active deamon present */
79 exit(0);
c6d1c018 80 log("cannot lock %s", LO);
df7d5679
RC
81 exit(1);
82 }
adec4d9e 83 ftruncate(lfd, 0);
df7d5679
RC
84 /*
85 * write process id for others to know
86 */
87 sprintf(line, "%u\n", pid);
88 pidoff = i = strlen(line);
88f026f7 89 if (write(lfd, line, i) != i) {
df7d5679 90 log("cannot write daemon pid");
df7d5679
RC
91 exit(1);
92 }
df7d5679
RC
93 /*
94 * search the spool directory for work and sort by queue order.
95 */
df7d5679
RC
96 if ((nitems = getq(&queue)) < 0) {
97 log("can't scan spool directory %s", SD);
98 exit(1);
99 }
88f026f7 100 if (nitems == 0) /* no work to do */
df7d5679 101 exit(0);
c6d1c018
RC
102 if (stb.st_mode & 01) { /* reset queue flag */
103 if (fchmod(lfd, stb.st_mode & 0776) < 0)
104 log("cannot chmod %s", LO);
105 }
88f026f7
RC
106 openpr(); /* open printer or remote */
107again:
df7d5679
RC
108 /*
109 * we found something to do now do it --
110 * write the name of the current control file into the lock file
111 * so the spool queue program can tell what we're working on
112 */
113 for (qp = queue; nitems--; free((char *) q)) {
114 q = *qp++;
115 if (stat(q->q_name, &stb) < 0)
116 continue;
88f026f7 117 restart:
df7d5679
RC
118 (void) lseek(lfd, pidoff, 0);
119 (void) sprintf(line, "%s\n", q->q_name);
120 i = strlen(line);
121 if (write(lfd, line, i) != i)
122 log("can't write (%d) control file name", errno);
123 if (!remote)
124 i = printit(q->q_name);
125 else
126 i = sendit(q->q_name);
88f026f7 127 /*
c6d1c018
RC
128 * Check to see if we are supposed to stop printing or
129 * if we are to rebuild the queue.
88f026f7 130 */
c6d1c018
RC
131 if (fstat(lfd, &stb) == 0) {
132 if (stb.st_mode & 0100)
133 goto done;
134 if (stb.st_mode & 01) {
135 for (free((char *) q); nitems--; free((char *) q))
136 q = *qp++;
137 if (fchmod(lfd, stb.st_mode & 0776) < 0)
138 log("cannot chmod %s", LO);
139 break;
140 }
141 }
845f1693
RC
142 if (i == 0) /* file ok and printed */
143 count++;
144 else if (i > 0) { /* try reprinting the job */
df7d5679
RC
145 log("restarting");
146 if (ofilter > 0) {
147 kill(ofilter, SIGCONT); /* to be sure */
148 (void) close(ofd);
149 while ((i = wait(0)) > 0 && i != ofilter)
150 ;
151 ofilter = 0;
152 }
88f026f7
RC
153 (void) close(pfd); /* close printer */
154 (void) lseek(lfd, pidoff, 0);
155 if (write(lfd, "\n", 1) != 1)
156 log("can't write (%d) control file name", errno);
157 openpr(); /* try to reopen printer */
df7d5679
RC
158 goto restart;
159 }
160 }
161 free((char *) queue);
88f026f7
RC
162 /*
163 * search the spool directory for more work.
164 */
165 if ((nitems = getq(&queue)) < 0) {
166 log("can't scan spool directory %s", SD);
167 exit(1);
168 }
169 if (nitems == 0) { /* no more work to do */
170 done:
845f1693
RC
171 if (count > 0) { /* Files actually printed */
172 if (!SF && !tof)
173 (void) write(ofd, FF, strlen(FF));
174 if (TR != NULL) /* output trailer */
175 (void) write(ofd, TR, strlen(TR));
176 }
88f026f7
RC
177 exit(0);
178 }
df7d5679
RC
179 goto again;
180}
181
182char fonts[4][50]; /* fonts for troff */
183
184static char ifonts[4][18] = {
185 "/usr/lib/vfont/R",
186 "/usr/lib/vfont/I",
187 "/usr/lib/vfont/B",
188 "/usr/lib/vfont/S"
189};
190
191/*
192 * The remaining part is the reading of the control file (cf)
193 * and performing the various actions.
194 * Returns 0 if everthing was OK, 1 if we should try to reprint the job and
195 * -1 if a non-recoverable error occured.
196 */
54266d1f 197static
df7d5679
RC
198printit(file)
199 char *file;
200{
201 register int i;
202 int bombed = 0;
203
204 /*
205 * open control file
206 */
207 if ((cfp = fopen(file, "r")) == NULL) {
208 log("control file (%s) open failure <errno = %d>", file, errno);
209 return(0);
210 }
211 /*
212 * Reset troff fonts.
213 */
214 for (i = 0; i < 4; i++)
215 strcpy(fonts[i], ifonts[i]);
216
217 /*
218 * read the control file for work to do
219 *
220 * file format -- first character in the line is a command
221 * rest of the line is the argument.
222 * valid commands are:
223 *
224 * J -- "job name" on banner page
225 * C -- "class name" on banner page
226 * L -- "literal" user's name to print on banner
227 * T -- "title" for pr
228 * H -- "host name" of machine where lpr was done
229 * P -- "person" user's login name
6bd17dac 230 * I -- "indent" amount to indent output
df7d5679
RC
231 * f -- "file name" name of text file to print
232 * l -- "file name" text file with control chars
233 * p -- "file name" text file to print with pr(1)
234 * t -- "file name" troff(1) file to print
a4f59913 235 * n -- "file name" ditroff(1) file to print
df7d5679
RC
236 * d -- "file name" dvi file to print
237 * g -- "file name" plot(1G) file to print
238 * v -- "file name" plain raster file to print
239 * c -- "file name" cifplot file to print
240 * 1 -- "R font file" for troff
241 * 2 -- "I font file" for troff
242 * 3 -- "B font file" for troff
243 * 4 -- "S font file" for troff
244 * N -- "name" of file (used by lpq)
245 * U -- "unlink" name of file to remove
246 * (after we print it. (Pass 2 only)).
247 * M -- "mail" to user when done printing
248 *
249 * getline reads a line and expands tabs to blanks
250 */
251
252 /* pass 1 */
253
254 while (getline(cfp))
255 switch (line[0]) {
256 case 'H':
845f1693 257 strcpy(fromhost, line+1);
df7d5679 258 if (class[0] == '\0')
731697c1 259 strncpy(class, line+1, sizeof(class)-1);
df7d5679
RC
260 continue;
261
262 case 'P':
731697c1 263 strncpy(logname, line+1, sizeof(logname)-1);
88f026f7
RC
264 if (RS) { /* restricted */
265 if (getpwnam(logname) == (struct passwd *)0) {
266 bombed = 2;
267 sendmail(bombed);
268 goto pass2;
269 }
270 }
df7d5679
RC
271 continue;
272
273 case 'J':
274 if (line[1] != '\0')
731697c1 275 strncpy(jobname, line+1, sizeof(jobname)-1);
df7d5679
RC
276 else
277 strcpy(jobname, " ");
278 continue;
279
280 case 'C':
281 if (line[1] != '\0')
731697c1 282 strncpy(class, line+1, sizeof(class)-1);
df7d5679
RC
283 else if (class[0] == '\0')
284 gethostname(class, sizeof (class));
285 continue;
286
287 case 'T': /* header title for pr */
731697c1 288 strncpy(title, line+1, sizeof(title)-1);
df7d5679
RC
289 continue;
290
291 case 'L': /* identification line */
292 if (!SH)
293 banner(line+1, jobname);
294 continue;
295
296 case '1': /* troff fonts */
297 case '2':
298 case '3':
299 case '4':
300 if (line[1] != '\0')
301 strcpy(fonts[line[0]-'1'], line+1);
302 continue;
303
304 case 'W': /* page width */
731697c1 305 strncpy(width+2, line+1, sizeof(width)-3);
df7d5679
RC
306 continue;
307
6bd17dac 308 case 'I': /* indent amount */
731697c1 309 strncpy(indent+2, line+1, sizeof(indent)-3);
6bd17dac
RC
310 continue;
311
df7d5679
RC
312 default: /* some file to print */
313 if ((i = print(line[0], line+1)) > 0) {
314 (void) fclose(cfp);
315 return(1);
316 } else if (i < 0)
317 bombed = 1;
318 title[0] = '\0';
319 continue;
320
df7d5679
RC
321 case 'N':
322 case 'U':
323 case 'M':
324 continue;
325 }
326
327 /* pass 2 */
328
88f026f7 329pass2:
df7d5679
RC
330 fseek(cfp, 0L, 0);
331 while (getline(cfp))
332 switch (line[0]) {
333 case 'M':
88f026f7
RC
334 if (bombed != 2) /* already sent if 2 */
335 sendmail(bombed);
df7d5679
RC
336 continue;
337
338 case 'U':
339 (void) unlink(line+1);
340 }
341 /*
342 * clean-up incase another control file exists
343 */
344 (void) fclose(cfp);
345 (void) unlink(file);
845f1693 346 return(bombed ? -1 : 0);
df7d5679
RC
347}
348
349/*
350 * Print a file.
a4f59913 351 * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
df7d5679
RC
352 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
353 * 0 if all is well.
354 * Note: all filters take stdin as the file, stdout as the printer,
355 * stderr as the log file, and must not ignore SIGINT.
356 */
54266d1f 357static
df7d5679
RC
358print(format, file)
359 int format;
360 char *file;
361{
362 register int n, fi, fo;
363 register char *prog;
364 char *av[15], buf[BUFSIZ];
365 int pid, p[2], stopped = 0;
366 union wait status;
367
adec4d9e 368 if ((fi = open(file, O_RDONLY)) < 0) {
df7d5679
RC
369 log("%s: open failure <errno = %d>", file, errno);
370 return(-1);
371 }
372 if (!SF && !tof) { /* start on a fresh page */
373 (void) write(ofd, FF, strlen(FF));
374 tof = 1;
375 }
376 if (IF == NULL && (format == 'f' || format == 'l')) {
377 tof = 0;
378 while ((n = read(fi, buf, BUFSIZ)) > 0)
379 if (write(ofd, buf, n) != n) {
380 (void) close(fi);
381 return(1);
382 }
383 (void) close(fi);
384 return(0);
385 }
386 switch (format) {
387 case 'p': /* print file using 'pr' */
388 if (IF == NULL) { /* use output filter */
389 prog = PR;
390 av[0] = "pr";
391 av[1] = width;
392 av[2] = length;
393 av[3] = "-h";
394 av[4] = *title ? title : " ";
395 av[5] = 0;
396 fo = ofd;
397 goto start;
398 }
399 pipe(p);
400 if ((prchild = dofork(DORETURN)) == 0) { /* child */
401 dup2(fi, 0); /* file is stdin */
402 dup2(p[1], 1); /* pipe is stdout */
403 for (n = 3; n < NOFILE; n++)
404 (void) close(n);
405 execl(PR, "pr", width, length, "-h", *title ? title : " ", 0);
406 log("cannot execl %s", PR);
407 exit(2);
408 }
409 (void) close(p[1]); /* close output side */
410 (void) close(fi);
411 if (prchild < 0) {
412 prchild = 0;
413 (void) close(p[0]);
414 return(-1);
415 }
416 fi = p[0]; /* use pipe for input */
417 case 'f': /* print plain text file */
418 prog = IF;
419 av[1] = width;
420 av[2] = length;
6bd17dac
RC
421 av[3] = indent;
422 n = 4;
df7d5679
RC
423 break;
424 case 'l': /* like 'f' but pass control characters */
425 prog = IF;
62a109c4 426 av[1] = "-c";
df7d5679
RC
427 av[2] = width;
428 av[3] = length;
6bd17dac
RC
429 av[4] = indent;
430 n = 5;
df7d5679 431 break;
88f026f7
RC
432 case 'r': /* print a fortran text file */
433 prog = RF;
434 av[1] = width;
435 av[2] = length;
436 n = 3;
437 break;
df7d5679 438 case 't': /* print troff output */
a4f59913 439 case 'n': /* print ditroff output */
88f026f7 440 case 'd': /* print tex output */
df7d5679 441 (void) unlink(".railmag");
88f026f7 442 if ((fo = creat(".railmag", FILMOD)) < 0) {
df7d5679
RC
443 log("cannot create .railmag");
444 (void) unlink(".railmag");
445 } else {
446 for (n = 0; n < 4; n++) {
447 if (fonts[n][0] != '/')
448 (void) write(fo, "/usr/lib/vfont/", 15);
449 (void) write(fo, fonts[n], strlen(fonts[n]));
450 (void) write(fo, "\n", 1);
451 }
452 (void) close(fo);
453 }
a4f59913 454 prog = (format == 't') ? TF : (format == 'n') ? NF : DF;
88f026f7
RC
455 av[1] = pxwidth;
456 av[2] = pxlength;
457 n = 3;
df7d5679
RC
458 break;
459 case 'c': /* print cifplot output */
460 prog = CF;
88f026f7
RC
461 av[1] = pxwidth;
462 av[2] = pxlength;
463 n = 3;
df7d5679
RC
464 break;
465 case 'g': /* print plot(1G) output */
466 prog = GF;
88f026f7
RC
467 av[1] = pxwidth;
468 av[2] = pxlength;
469 n = 3;
df7d5679
RC
470 break;
471 case 'v': /* print raster output */
472 prog = VF;
88f026f7
RC
473 av[1] = pxwidth;
474 av[2] = pxlength;
475 n = 3;
df7d5679
RC
476 break;
477 default:
478 (void) close(fi);
479 log("illegal format character '%c'", format);
480 return(-1);
481 }
482 if ((av[0] = rindex(prog, '/')) != NULL)
483 av[0]++;
484 else
485 av[0] = prog;
486 av[n++] = "-n";
487 av[n++] = logname;
488 av[n++] = "-h";
845f1693 489 av[n++] = fromhost;
df7d5679
RC
490 av[n++] = AF;
491 av[n] = 0;
492 fo = pfd;
493 if (ofilter > 0) { /* stop output filter */
494 write(ofd, "\031\1", 2);
495 while ((pid = wait3(&status, WUNTRACED, 0)) > 0 && pid != ofilter)
496 ;
497 if (status.w_stopval != WSTOPPED) {
498 (void) close(fi);
499 log("output filter died (%d)", status.w_retcode);
500 return(1);
501 }
502 stopped++;
503 }
504start:
505 if ((child = dofork(DORETURN)) == 0) { /* child */
506 dup2(fi, 0);
507 dup2(fo, 1);
508 for (n = 3; n < NOFILE; n++)
509 (void) close(n);
510 execv(prog, av);
511 log("cannot execl %s", prog);
512 exit(2);
513 }
514 (void) close(fi);
515 if (child < 0)
516 status.w_retcode = 100;
517 else
518 while ((pid = wait(&status)) > 0 && pid != child)
519 ;
520 child = 0;
521 prchild = 0;
522 if (stopped) { /* restart output filter */
523 if (kill(ofilter, SIGCONT) < 0) {
524 log("cannot restart output filter");
525 exit(1);
526 }
527 }
528 tof = 0;
529 if (!WIFEXITED(status) || status.w_retcode > 1) {
530 log("Daemon Filter '%c' Malfunction (%d)", format, status.w_retcode);
531 return(-1);
532 } else if (status.w_retcode == 1)
533 return(1);
534 tof = 1;
535 return(0);
536}
537
538/*
539 * Send the daemon control file (cf) and any data files.
540 * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
541 * 0 if all is well.
542 */
54266d1f 543static
df7d5679
RC
544sendit(file)
545 char *file;
546{
547 register int linelen, err = 0;
548 char last[132];
549
550 /*
551 * open control file
552 */
553 if ((cfp = fopen(file, "r")) == NULL) {
554 log("control file (%s) open failure <errno = %d>", file, errno);
555 return(0);
556 }
557 /*
558 * read the control file for work to do
559 *
560 * file format -- first character in the line is a command
561 * rest of the line is the argument.
562 * commands of interest are:
563 *
564 * a-z -- "file name" name of file to print
565 * U -- "unlink" name of file to remove
566 * (after we print it. (Pass 2 only)).
567 */
568
569 /*
570 * pass 1
571 */
572 while (getline(cfp)) {
573 again:
574 if (line[0] >= 'a' && line[0] <= 'z') {
575 strcpy(last, line);
576 while (linelen = getline(cfp))
577 if (strcmp(last, line))
578 break;
579 if ((err = sendfile('\3', last+1)) > 0) {
580 (void) fclose(cfp);
581 return(1);
582 } else if (err)
583 break;
584 if (linelen)
585 goto again;
586 break;
587 }
588 }
589 if (!err && sendfile('\2', file) > 0) {
590 (void) fclose(cfp);
591 return(1);
592 }
593 /*
594 * pass 2
595 */
596 fseek(cfp, 0L, 0);
597 while (getline(cfp))
598 if (line[0] == 'U')
599 (void) unlink(line+1);
600 /*
601 * clean-up incase another control file exists
602 */
603 (void) fclose(cfp);
604 (void) unlink(file);
605 return(0);
606}
607
608/*
609 * Send a data file to the remote machine and spool it.
610 * Return positive if we should try resending.
611 */
54266d1f 612static
df7d5679
RC
613sendfile(type, file)
614 char type, *file;
615{
616 register int f, i, amt;
617 struct stat stb;
618 char buf[BUFSIZ];
619 int sizerr;
620
adec4d9e 621 if ((f = open(file, O_RDONLY)) < 0 || fstat(f, &stb) < 0) {
df7d5679
RC
622 log("file (%s) open failure <errno = %d>", file, errno);
623 return(-1);
624 }
625 (void) sprintf(buf, "%c%d %s\n", type, stb.st_size, file);
626 amt = strlen(buf);
48ca7288
RC
627 if (write(pfd, buf, amt) != amt) {
628 (void) close(f);
df7d5679 629 return(1);
48ca7288
RC
630 }
631 if (noresponse()) {
632 (void) close(f);
df7d5679 633 return(1);
48ca7288 634 }
df7d5679
RC
635 sizerr = 0;
636 for (i = 0; i < stb.st_size; i += BUFSIZ) {
637 amt = BUFSIZ;
638 if (i + amt > stb.st_size)
639 amt = stb.st_size - i;
640 if (sizerr == 0 && read(f, buf, amt) != amt)
641 sizerr = 1;
48ca7288
RC
642 if (write(pfd, buf, amt) != amt) {
643 (void) close(f);
df7d5679 644 return(1);
48ca7288 645 }
df7d5679
RC
646 }
647 (void) close(f);
648 if (sizerr) {
649 log("%s: changed size", file);
650 (void) write(pfd, "\1", 1); /* tell recvjob to ignore this file */
651 return(-1);
652 }
653 if (write(pfd, "", 1) != 1)
654 return(1);
655 if (noresponse())
656 return(1);
657 return(0);
658}
659
660/*
661 * Check to make sure there have been no errors and that both programs
662 * are in sync with eachother.
663 * Return non-zero if the connection was lost.
664 */
665static
666noresponse()
667{
668 char resp;
669
670 if (read(pfd, &resp, 1) != 1 || resp != '\0') {
671 log("lost connection or error in recvjob");
672 return(1);
673 }
674 return(0);
675}
676
677/*
678 * Banner printing stuff
679 */
54266d1f 680static
df7d5679
RC
681banner(name1, name2)
682 char *name1, *name2;
683{
684 time_t tvec;
685 extern char *ctime();
686
687 time(&tvec);
688 if (!SF && !tof)
689 (void) write(ofd, FF, strlen(FF));
690 if (SB) { /* short banner only */
691 if (class[0]) {
692 (void) write(ofd, class, strlen(class));
693 (void) write(ofd, ":", 1);
694 }
695 (void) write(ofd, name1, strlen(name1));
696 (void) write(ofd, " Job: ", 7);
697 (void) write(ofd, name2, strlen(name2));
698 (void) write(ofd, " Date: ", 8);
699 (void) write(ofd, ctime(&tvec), 24);
700 (void) write(ofd, "\n", 1);
701 } else { /* normal banner */
702 (void) write(ofd, "\n\n\n", 3);
703 scan_out(ofd, name1, '\0');
704 (void) write(ofd, "\n\n", 2);
705 scan_out(ofd, name2, '\0');
706 if (class[0]) {
707 (void) write(ofd,"\n\n\n",3);
708 scan_out(ofd, class, '\0');
709 }
710 (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
711 (void) write(ofd, name2, strlen(name2));
712 (void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
713 (void) write(ofd, ctime(&tvec), 24);
714 (void) write(ofd, "\n", 1);
715 }
716 if (!SF)
717 (void) write(ofd, FF, strlen(FF));
718 tof = 1;
719}
720
54266d1f 721static char *
df7d5679
RC
722scnline(key, p, c)
723 register char key, *p;
724 char c;
725{
726 register scnwidth;
727
728 for (scnwidth = WIDTH; --scnwidth;) {
729 key <<= 1;
730 *p++ = key & 0200 ? c : BACKGND;
731 }
732 return (p);
733}
734
735#define TRC(q) (((q)-' ')&0177)
736
54266d1f 737static
df7d5679
RC
738scan_out(scfd, scsp, dlm)
739 int scfd;
740 char *scsp, dlm;
741{
742 register char *strp;
743 register nchrs, j;
744 char outbuf[LINELEN+1], *sp, c, cc;
745 int d, scnhgt;
746 extern char scnkey[][HEIGHT]; /* in lpdchar.c */
747
748 for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
749 strp = &outbuf[0];
750 sp = scsp;
751 for (nchrs = 0; ; ) {
752 d = dropit(c = TRC(cc = *sp++));
753 if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
754 for (j = WIDTH; --j;)
755 *strp++ = BACKGND;
756 else
757 strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
758 if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
759 break;
760 *strp++ = BACKGND;
761 *strp++ = BACKGND;
762 }
763 while (*--strp == BACKGND && strp >= outbuf)
764 ;
765 strp++;
766 *strp++ = '\n';
767 (void) write(scfd, outbuf, strp-outbuf);
768 }
769}
770
54266d1f 771static
df7d5679
RC
772dropit(c)
773 char c;
774{
775 switch(c) {
776
777 case TRC('_'):
778 case TRC(';'):
779 case TRC(','):
780 case TRC('g'):
781 case TRC('j'):
782 case TRC('p'):
783 case TRC('q'):
784 case TRC('y'):
785 return (DROP);
786
787 default:
788 return (0);
789 }
790}
791
792/*
793 * sendmail ---
794 * tell people about job completion
795 */
54266d1f 796static
df7d5679
RC
797sendmail(bombed)
798 int bombed;
799{
800 static int p[2];
801 register int i;
802 int stat;
803 register char *cp;
804 char buf[100];
805
806 pipe(p);
807 if ((stat = dofork(DORETURN)) == 0) { /* child */
808 dup2(p[0], 0);
809 for (i = 3; i < NOFILE; i++)
810 (void) close(i);
811 if ((cp = rindex(MAIL, '/')) != NULL)
812 cp++;
813 else
814 cp = MAIL;
845f1693 815 sprintf(buf, "%s@%s", line+1, fromhost);
df7d5679
RC
816 execl(MAIL, cp, buf, 0);
817 exit(0);
818 } else if (stat > 0) { /* parent */
819 dup2(p[1], 1);
845f1693 820 printf("To: %s@%s\n", line+1, fromhost);
df7d5679
RC
821 printf("Subject: printer job\n\n");
822 printf("Your printer job ");
823 if (*jobname)
824 printf("(%s) ", jobname);
88f026f7
RC
825 switch (bombed) {
826 case 0:
827 printf("\ncompleted successfully\n");
828 break;
829 default:
830 case 1:
831 printf("\ncould not be printed\n");
832 break;
833 case 2:
834 printf("\ncould not be printed without an account on %s\n", host);
835 break;
836 }
df7d5679
RC
837 fflush(stdout);
838 (void) close(1);
839 }
840 (void) close(p[0]);
841 (void) close(p[1]);
842 wait(&stat);
843}
844
845/*
846 * dofork - fork with retries on failure
847 */
54266d1f 848static
df7d5679
RC
849dofork(action)
850 int action;
851{
852 register int i, pid;
853
854 for (i = 0; i < 20; i++) {
88f026f7 855 if ((pid = fork()) < 0) {
df7d5679 856 sleep((unsigned)(i*i));
88f026f7
RC
857 continue;
858 }
859 /*
860 * Child should run as daemon instead of root
861 */
862 if (pid == 0)
863 setuid(DU);
864 return(pid);
df7d5679
RC
865 }
866 log("can't fork");
867
868 switch (action) {
869 case DORETURN:
870 return (-1);
871 default:
872 log("bad action (%d) to dofork", action);
873 /*FALL THRU*/
874 case DOABORT:
875 exit(1);
876 }
877 /*NOTREACHED*/
878}
879
880/*
f0b73bd5 881 * Cleanup child processes when a signal is caught.
df7d5679 882 */
54266d1f 883static
df7d5679
RC
884onintr()
885{
886 kill(0, SIGINT);
887 if (ofilter > 0)
888 kill(ofilter, SIGCONT);
889 while (wait(0) > 0)
890 ;
891 exit(0);
892}
893
54266d1f 894static
df7d5679
RC
895init()
896{
897 int status;
898
c6d1c018
RC
899 if ((status = pgetent(line, printer)) < 0)
900 fatal("can't open printer description file");
901 else if (status == 0)
902 fatal("unknown printer");
df7d5679
RC
903 if ((LP = pgetstr("lp", &bp)) == NULL)
904 LP = DEFDEVLP;
905 if ((RP = pgetstr("rp", &bp)) == NULL)
88f026f7 906 RP = DEFLP;
df7d5679
RC
907 if ((LO = pgetstr("lo", &bp)) == NULL)
908 LO = DEFLOCK;
909 if ((ST = pgetstr("st", &bp)) == NULL)
910 ST = DEFSTAT;
911 if ((LF = pgetstr("lf", &bp)) == NULL)
912 LF = DEFLOGF;
913 if ((SD = pgetstr("sd", &bp)) == NULL)
914 SD = DEFSPOOL;
915 if ((DU = pgetnum("du")) < 0)
916 DU = DEFUID;
917 if ((FF = pgetstr("ff", &bp)) == NULL)
918 FF = DEFFF;
919 if ((PW = pgetnum("pw")) < 0)
920 PW = DEFWIDTH;
921 sprintf(&width[2], "%d", PW);
922 if ((PL = pgetnum("pl")) < 0)
923 PL = DEFLENGTH;
924 sprintf(&length[2], "%d", PL);
88f026f7
RC
925 if ((PX = pgetnum("px")) < 0)
926 PX = 0;
927 sprintf(&pxwidth[2], "%d", PX);
928 if ((PY = pgetnum("py")) < 0)
929 PY = 0;
930 sprintf(&pxlength[2], "%d", PY);
df7d5679
RC
931 RM = pgetstr("rm", &bp);
932 AF = pgetstr("af", &bp);
933 OF = pgetstr("of", &bp);
934 IF = pgetstr("if", &bp);
88f026f7 935 RF = pgetstr("rf", &bp);
df7d5679 936 TF = pgetstr("tf", &bp);
a4f59913 937 NF = pgetstr("nf", &bp);
df7d5679
RC
938 DF = pgetstr("df", &bp);
939 GF = pgetstr("gf", &bp);
940 VF = pgetstr("vf", &bp);
941 CF = pgetstr("cf", &bp);
942 TR = pgetstr("tr", &bp);
88f026f7 943 RS = pgetflag("rs");
df7d5679
RC
944 SF = pgetflag("sf");
945 SH = pgetflag("sh");
946 SB = pgetflag("sb");
947 RW = pgetflag("rw");
948 BR = pgetnum("br");
949 if ((FC = pgetnum("fc")) < 0)
950 FC = 0;
951 if ((FS = pgetnum("fs")) < 0)
952 FS = 0;
953 if ((XC = pgetnum("xc")) < 0)
954 XC = 0;
955 if ((XS = pgetnum("xs")) < 0)
956 XS = 0;
6bd17dac 957 tof = !pgetflag("fo");
df7d5679
RC
958}
959
88f026f7
RC
960/*
961 * Acquire line printer or remote connection.
962 */
54266d1f 963static
88f026f7
RC
964openpr()
965{
966 register int i, n;
967
968 if (*LP) {
969 for (i = 1; ; i = i < 32 ? i << 1 : i) {
adec4d9e 970 pfd = open(LP, RW ? O_RDWR : O_WRONLY);
88f026f7
RC
971 if (pfd >= 0)
972 break;
973 if (errno == ENOENT) {
974 log("cannot open %s", LP);
975 exit(1);
976 }
977 if (i == 1)
978 status("waiting for %s to become ready (offline ?)", printer);
979 sleep(i);
980 }
981 if (isatty(pfd))
982 setty();
983 status("%s is ready and printing", printer);
984 } else if (RM != NULL) {
985 for (i = 1; ; i = i < 512 ? i << 1 : i) {
a87bde97 986 pfd = getport(RM);
88f026f7
RC
987 if (pfd >= 0) {
988 (void) sprintf(line, "\2%s\n", RP);
989 n = strlen(line);
990 if (write(pfd, line, n) != n)
991 break;
992 if (noresponse())
993 (void) close(pfd);
994 else
995 break;
996 }
997 if (i == 1)
998 status("waiting for %s to come up", RM);
999 sleep(i);
1000 }
1001 status("sending to %s", RM);
1002 remote = 1;
1003 } else {
1004 log("no line printer device or remote machine name");
1005 exit(1);
1006 }
1007 /*
1008 * Start up an output filter, if needed.
1009 */
1010 if (OF) {
1011 int p[2];
1012 char *cp;
1013
1014 pipe(p);
1015 if ((ofilter = dofork(DOABORT)) == 0) { /* child */
1016 dup2(p[0], 0); /* pipe is std in */
1017 dup2(pfd, 1); /* printer is std out */
1018 for (i = 3; i < NOFILE; i++)
1019 (void) close(i);
1020 if ((cp = rindex(OF, '/')) == NULL)
1021 cp = OF;
1022 else
1023 cp++;
1024 execl(OF, cp, width, length, 0);
1025 log("can't execl output filter %s", OF);
1026 exit(1);
1027 }
1028 (void) close(p[0]); /* close input side */
1029 ofd = p[1]; /* use pipe for output */
1030 } else {
1031 ofd = pfd;
1032 ofilter = 0;
1033 }
1034}
1035
df7d5679
RC
1036struct bauds {
1037 int baud;
1038 int speed;
1039} bauds[] = {
1040 50, B50,
1041 75, B75,
1042 110, B110,
1043 134, B134,
1044 150, B150,
1045 200, B200,
1046 300, B300,
1047 600, B600,
1048 1200, B1200,
1049 1800, B1800,
1050 2400, B2400,
1051 4800, B4800,
1052 9600, B9600,
1053 19200, EXTA,
1054 38400, EXTB,
1055 0, 0
1056};
1057
1058/*
1059 * setup tty lines.
1060 */
54266d1f 1061static
df7d5679
RC
1062setty()
1063{
1064 struct sgttyb ttybuf;
1065 register struct bauds *bp;
1066
1067 if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
1068 log("cannot set exclusive-use");
1069 exit(1);
1070 }
1071 if (ioctl(pfd, TIOCGETP, (char *)&ttybuf) < 0) {
1072 log("cannot get tty parameters");
1073 exit(1);
1074 }
1075 if (BR > 0) {
1076 for (bp = bauds; bp->baud; bp++)
1077 if (BR == bp->baud)
1078 break;
1079 if (!bp->baud) {
1080 log("illegal baud rate %d", BR);
1081 exit(1);
1082 }
1083 ttybuf.sg_ispeed = ttybuf.sg_ospeed = bp->speed;
1084 }
c6d1c018
RC
1085 ttybuf.sg_flags &= ~FC;
1086 ttybuf.sg_flags |= FS;
df7d5679
RC
1087 if (ioctl(pfd, TIOCSETP, (char *)&ttybuf) < 0) {
1088 log("cannot set tty parameters");
1089 exit(1);
1090 }
1091 if (XC) {
1092 if (ioctl(pfd, TIOCLBIC, &XC) < 0) {
1093 log("cannot set local tty parameters");
1094 exit(1);
1095 }
1096 }
1097 if (XS) {
1098 if (ioctl(pfd, TIOCLBIS, &XS) < 0) {
1099 log("cannot set local tty parameters");
1100 exit(1);
1101 }
1102 }
1103}
88f026f7
RC
1104
1105/*VARARGS1*/
1106static
1107status(msg, a1, a2, a3)
1108 char *msg;
1109{
1110 register int fd;
1111 char buf[BUFSIZ];
1112
1113 umask(0);
adec4d9e
SL
1114 fd = open(ST, O_WRONLY|O_CREAT, 0664);
1115 if (fd < 0 || flock(fd, LOCK_EX) < 0)
88f026f7 1116 fatal("cannot create status file");
adec4d9e 1117 ftruncate(fd, 0);
88f026f7
RC
1118 sprintf(buf, msg, a1, a2, a3);
1119 strcat(buf, "\n");
1120 (void) write(fd, buf, strlen(buf));
1121 (void) close(fd);
1122}