xref: /original-bsd/usr.sbin/lpr/lpr/lpr.c (revision 3708840b)
1 /*	lpr.c	4.17	83/05/18	*/
2 /*
3  *      lpr -- off line print
4  *
5  * Allows multiple printers and printers on remote machines by
6  * using information from a printer data base.
7  */
8 
9 #include "lp.h"
10 
11 char    *tfname;		/* tmp copy of cf before linking */
12 char    *cfname;		/* daemon control files, linked from tf's */
13 char    *dfname;		/* data files */
14 
15 int	nact;			/* number of jobs to act on */
16 int	tfd;			/* control file descriptor */
17 int     mailflg;		/* send mail */
18 int	qflag;			/* q job, but don't exec daemon */
19 char	format = 'f';		/* format char for printing files */
20 int	rflag;			/* remove files upon completion */
21 int	sflag;			/* symbolic link flag */
22 int	inchar;			/* location to increment char in file names */
23 int     ncopies = 1;		/* # of copies to make */
24 int	iflag;			/* indentation wanted */
25 int	indent;			/* amount to indent */
26 int	hdr = 1;		/* print header or not (default is yes) */
27 int     userid;			/* user id */
28 char	*person;		/* user name */
29 char	*title;			/* pr'ing title */
30 char	*fonts[4];		/* troff font names */
31 char	*width;			/* width for versatec printing */
32 char	*class = host;		/* class title on header page */
33 char    *jobname;		/* job name on header page */
34 
35 char	*linked();
36 int	cleanup();
37 
38 /*ARGSUSED*/
39 main(argc, argv)
40 	int argc;
41 	char *argv[];
42 {
43 	extern struct passwd *getpwuid();
44 	struct passwd *pw;
45 	extern char *itoa();
46 	register char *arg, *cp;
47 	int i, f;
48 	struct stat stb;
49 
50 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
51 		signal(SIGHUP, cleanup);
52 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
53 		signal(SIGINT, cleanup);
54 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
55 		signal(SIGQUIT, cleanup);
56 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
57 		signal(SIGTERM, cleanup);
58 
59 	name = argv[0];
60 	gethostname(host, sizeof (host));
61 
62 	while (argc > 1 && argv[1][0] == '-') {
63 		argc--;
64 		arg = *++argv;
65 		switch (arg[1]) {
66 
67 		case 'P':		/* specifiy printer name */
68 			printer = &arg[2];
69 			break;
70 
71 		case 'C':		/* classification spec */
72 			hdr++;
73 			if (arg[2])
74 				class = &arg[2];
75 			else if (argc > 1) {
76 				argc--;
77 				class = *++argv;
78 			}
79 			break;
80 
81 		case 'J':		/* job name */
82 			hdr++;
83 			if (arg[2])
84 				jobname = &arg[2];
85 			else if (argc > 1) {
86 				argc--;
87 				jobname = *++argv;
88 			}
89 			break;
90 
91 		case 'T':		/* pr's title line */
92 			if (arg[2])
93 				title = &arg[2];
94 			else if (argc > 1) {
95 				argc--;
96 				title = *++argv;
97 			}
98 			break;
99 
100 		case 'l':		/* literal output */
101 		case 'p':		/* print using ``pr'' */
102 		case 't':		/* print troff output (cat files) */
103 		case 'd':		/* print tex output (dvi files) */
104 		case 'g':		/* print graph(1G) output */
105 		case 'c':		/* print cifplot output */
106 		case 'v':		/* print vplot output */
107 			format = arg[1];
108 			break;
109 
110 		case 'f':		/* print fortran output */
111 			format = 'r';
112 			break;
113 
114 		case '4':		/* troff fonts */
115 		case '3':
116 		case '2':
117 		case '1':
118 			if (argc > 1) {
119 				argc--;
120 				fonts[arg[1] - '1'] = *++argv;
121 				format = 't';
122 			}
123 			break;
124 
125 		case 'w':		/* versatec page width */
126 			width = arg+2;
127 			break;
128 
129 		case 'r':		/* remove file when done */
130 			rflag++;
131 			break;
132 
133 		case 'm':		/* send mail when done */
134 			mailflg++;
135 			break;
136 
137 		case 'h':		/* toggle want of header page */
138 			hdr = !hdr;
139 			break;
140 
141 		case 's':		/* try to link files */
142 			sflag++;
143 			break;
144 
145 		case 'q':		/* just q job */
146 			qflag++;
147 			break;
148 
149 		case 'i':		/* indent output */
150 			iflag++;
151 			indent = arg[2] ? atoi(&arg[2]) : 8;
152 			break;
153 
154 		case '#':		/* n copies */
155 			if (isdigit(arg[2]))
156 				ncopies = atoi(&arg[2]);
157 		}
158 	}
159 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
160 		printer = DEFLP;
161 	chkprinter(printer);
162 	/*
163 	 * Check to make sure queuing is enabled.
164 	 */
165 	(void) sprintf(line, "%s/%s", SD, LO);
166 	if (stat(line, &stb) == 0 && (stb.st_mode & 010))
167 		fatal("Printer queue is disabled");
168 	/*
169 	 * Get the identity of the person doing the lpr using the same
170 	 * algorithm as lprm.
171 	 */
172 	userid = getuid();
173 	if ((pw = getpwuid(userid)) == NULL)
174 		fatal("Who are you?");
175 	person = pw->pw_name;
176 	/*
177 	 * Initialize the control file.
178 	 */
179 	mktemps();
180 	tfd = nfile(tfname);
181 	card('H', host);
182 	card('P', person);
183 	if (hdr) {
184 		if (jobname == NULL) {
185 			if (argc == 1)
186 				jobname = "stdin";
187 			else
188 				jobname = (arg = rindex(argv[1], '/')) ? arg+1 : argv[1];
189 		}
190 		card('J', jobname);
191 		card('C', class);
192 		card('L', person);
193 	}
194 	if (iflag)
195 		card('I', itoa(indent));
196 	if (mailflg)
197 		card('M', person);
198 	if (format == 't' || format == 'd')
199 		for (i = 0; i < 4; i++)
200 			if (fonts[i] != NULL)
201 				card('1'+i, fonts[i]);
202 	if (width != NULL)
203 		card('W', width);
204 
205 	/*
206 	 * Read the files and spool them.
207 	 */
208 	if (argc == 1)
209 		copy(0, " ");
210 	else while (--argc) {
211 		if ((f = test(arg = *++argv)) < 0)
212 			continue;	/* file unreasonable */
213 
214 		if (sflag && (cp = linked(arg)) != NULL) {
215 			if (format == 'p')
216 				card('T', title ? title : arg);
217 			for (i = 0; i < ncopies; i++)
218 				card(format, &dfname[inchar-2]);
219 			card('U', &dfname[inchar-2]);
220 			if (f)
221 				card('U', cp);
222 			card('N', arg);
223 			dfname[inchar]++;
224 			nact++;
225 			continue;
226 		}
227 		if (sflag)
228 			printf("%s: %s: not linked, copying instead\n", name, arg);
229 		if ((i = open(arg, 0)) < 0) {
230 			printf("%s: cannot open %s\n", name, arg);
231 			continue;
232 		}
233 		copy(i, arg);
234 		(void) close(i);
235 		if (f && unlink(arg) < 0)
236 			printf("%s: %s: not removed\n", name, arg);
237 	}
238 
239 	if (nact) {
240 		(void) close(tfd);
241 		tfname[inchar]--;
242 		/*
243 		 * Touch the control file to fix position in the queue.
244 		 */
245 		if ((tfd = open(tfname, 2)) >= 0) {
246 			char c;
247 
248 			if (read(tfd, &c, 1) == 1 && lseek(tfd, 0L, 0) == 0 &&
249 			    write(tfd, &c, 1) != 1) {
250 				printf("%s: cannot touch %s\n", name, tfname);
251 				tfname[inchar]++;
252 				cleanup();
253 			}
254 			(void) close(tfd);
255 		}
256 		if (link(tfname, cfname) < 0) {
257 			printf("%s: cannot rename %s\n", name, cfname);
258 			tfname[inchar]++;
259 			cleanup();
260 		}
261 		unlink(tfname);
262 		if (qflag)		/* just q things up */
263 			exit(0);
264 		if (!startdaemon())
265 			printf("jobs queued, but cannot start daemon.\n");
266 		exit(0);
267 	}
268 	cleanup();
269 	/* NOTREACHED */
270 }
271 
272 /*
273  * Create the file n and copy from file descriptor f.
274  */
275 copy(f, n)
276 	int f;
277 	char n[];
278 {
279 	register int fd, i, nr, nc;
280 	char buf[BUFSIZ];
281 
282 	if (format == 'p')
283 		card('T', title ? title : n);
284 	for (i = 0; i < ncopies; i++)
285 		card(format, &dfname[inchar-2]);
286 	card('U', &dfname[inchar-2]);
287 	card('N', n);
288 	fd = nfile(dfname);
289 	nr = nc = 0;
290 	while ((i = read(f, buf, BUFSIZ)) > 0) {
291 		if (write(fd, buf, i) != i) {
292 			printf("%s: %s: temp file write error\n", name, n);
293 			break;
294 		}
295 		nc += i;
296 		if (nc >= BUFSIZ) {
297 			nc -= BUFSIZ;
298 			nr++;
299 			if (MX > 0 && nr > MX) {
300 				printf("%s: %s: copy file is too large\n", name, n);
301 				break;
302 			}
303 		}
304 	}
305 	(void) close(fd);
306 	nact++;
307 }
308 
309 /*
310  * Try and link the file to dfname. Return a pointer to the full
311  * path name if successful.
312  */
313 char *
314 linked(file)
315 	register char *file;
316 {
317 	register char *cp;
318 	static char buf[BUFSIZ];
319 
320 	if (*file != '/') {
321 		if (getwd(buf) == NULL)
322 			return(NULL);
323 		while (file[0] == '.') {
324 			switch (file[1]) {
325 			case '/':
326 				file += 2;
327 				continue;
328 			case '.':
329 				if (file[2] == '/') {
330 					if ((cp = rindex(buf, '/')) != NULL)
331 						*cp = '\0';
332 					file += 3;
333 					continue;
334 				}
335 			}
336 			break;
337 		}
338 		strcat(buf, "/");
339 		strcat(buf, file);
340 		file = buf;
341 	}
342 	return(symlink(file, dfname) ? NULL : file);
343 }
344 
345 /*
346  * Put a line into the control file.
347  */
348 card(c, p2)
349 	register char c, *p2;
350 {
351 	char buf[BUFSIZ];
352 	register char *p1 = buf;
353 	register int len = 2;
354 
355 	*p1++ = c;
356 	while ((c = *p2++) != '\0') {
357 		*p1++ = c;
358 		len++;
359 	}
360 	*p1++ = '\n';
361 	write(tfd, buf, len);
362 }
363 
364 /*
365  * Create a new file in the spool directory.
366  */
367 nfile(n)
368 	char *n;
369 {
370 	register f;
371 	int oldumask = umask(0);		/* should block signals */
372 
373 	f = creat(n, FILMOD);
374 	(void) umask(oldumask);
375 	if (f < 0) {
376 		printf("%s: cannot create %s\n", name, n);
377 		cleanup();
378 	}
379 	if (++n[inchar] > 'z') {
380 		if (++n[inchar-2] == 't') {
381 			printf("too many files - break up the job\n");
382 			cleanup();
383 		}
384 		n[inchar] = 'A';
385 	} else if (n[inchar] == '[')
386 		n[inchar] = 'a';
387 	return(f);
388 }
389 
390 /*
391  * Cleanup after interrupts and errors.
392  */
393 cleanup()
394 {
395 	register i;
396 
397 	signal(SIGHUP, SIG_IGN);
398 	signal(SIGINT, SIG_IGN);
399 	signal(SIGQUIT, SIG_IGN);
400 	signal(SIGTERM, SIG_IGN);
401 	i = inchar;
402 	if (tfname)
403 		do
404 			unlink(tfname);
405 		while (tfname[i]-- != 'A');
406 	if (cfname)
407 		do
408 			unlink(cfname);
409 		while (cfname[i]-- != 'A');
410 	if (dfname)
411 		do {
412 			do
413 				unlink(dfname);
414 			while (dfname[i]-- != 'A');
415 			dfname[i] = 'z';
416 		} while (dfname[i-2]-- != 'd');
417 	exit(1);
418 }
419 
420 /*
421  * Test to see if this is a printable file.
422  * Return -1 if it is not, 0 if its printable, and 1 if
423  * we should remove it after printing.
424  */
425 test(file)
426 	char *file;
427 {
428 	struct exec execb;
429 	struct stat statb;
430 	register int fd;
431 	register char *cp;
432 
433 	if (access(file, 4) < 0) {
434 		printf("%s: cannot access %s\n", name, file);
435 		return(-1);
436 	}
437 	if (stat(file, &statb) < 0) {
438 		printf("%s: cannot stat %s\n", name, file);
439 		return(-1);
440 	}
441 	if ((statb.st_mode & S_IFMT) == S_IFDIR) {
442 		printf("%s: %s is a directory\n", name, file);
443 		return(-1);
444 	}
445 	if ((fd = open(file, 0)) < 0) {
446 		printf("%s: cannot open %s\n", name, file);
447 		return(-1);
448 	}
449 	if (read(fd, &execb, sizeof(execb)) == sizeof(execb))
450 		switch(execb.a_magic) {
451 		case A_MAGIC1:
452 		case A_MAGIC2:
453 		case A_MAGIC3:
454 #ifdef A_MAGIC4
455 		case A_MAGIC4:
456 #endif
457 			printf("%s: %s is an executable program", name, file);
458 			goto error1;
459 
460 		case ARMAG:
461 			printf("%s: %s is an archive file", name, file);
462 			goto error1;
463 		}
464 	(void) close(fd);
465 	if (rflag) {
466 		if ((cp = rindex(file, '/')) == NULL) {
467 			if (access(".", 2) == 0)
468 				return(1);
469 		} else {
470 			*cp = '\0';
471 			fd = access(file, 2);
472 			*cp = '/';
473 			if (fd == 0)
474 				return(1);
475 		}
476 		printf("%s: %s: is not removable by you\n", name, file);
477 	}
478 	return(0);
479 
480 error1:
481 	printf(" and is unprintable\n");
482 	(void) close(fd);
483 	return(-1);
484 }
485 
486 /*
487  * itoa - integer to string conversion
488  */
489 char *
490 itoa(i)
491 	register int i;
492 {
493 	static char b[10] = "########";
494 	register char *p;
495 
496 	p = &b[8];
497 	do
498 		*p-- = i%10 + '0';
499 	while (i /= 10);
500 	return(++p);
501 }
502 
503 /*
504  * Perform lookup for printer name or abbreviation --
505  */
506 chkprinter(s)
507 	register char *s;
508 {
509 	int status;
510 
511 	if ((status = pgetent(line, s)) < 0)
512 		fatal("cannot open printer description file");
513 	else if (status == 0)
514 		fatal("unknown printer");
515 	if ((SD = pgetstr("sd", &bp)) == NULL)
516 		SD = DEFSPOOL;
517 	if ((LO = pgetstr("lo", &bp)) == NULL)
518 		LO = DEFLOCK;
519 	if ((MX = pgetnum("mx")) < 0)
520 		MX = DEFMX;
521 	if ((DU = pgetnum("du")) < 0)
522 		DU = DEFUID;
523 }
524 
525 /*
526  * Make the temp files.
527  */
528 mktemps()
529 {
530 	register int c, len;
531 	int n;
532 	char buf[BUFSIZ], *mktemp();
533 	FILE *fp;
534 
535 	(void) sprintf(buf, "%s/.seq", SD);
536 	if ((fp = fopen(buf, "r+")) == NULL) {
537 		if ((fp = fopen(buf, "w")) == NULL) {
538 			printf("%s: cannot create %s\n", name, buf);
539 			exit(1);
540 		}
541 		setbuf(fp, buf);
542 		n = 0;
543 	} else {
544 		setbuf(fp, buf);
545 		if (flock(fileno(fp), FEXLOCK)) {
546 			printf("%s: cannot lock %s\n", name, buf);
547 			exit(1);
548 		}
549 		n = 0;
550 		while ((c = getc(fp)) >= '0' && c <= '9')
551 			n = n * 10 + (c - '0');
552 	}
553 	len = strlen(SD) + strlen(host) + 8;
554 	tfname = mktemp("tf", n, len);
555 	cfname = mktemp("cf", n, len);
556 	dfname = mktemp("df", n, len);
557 	inchar = strlen(SD) + 3;
558 	n = (n + 1) % 1000;
559 	(void) fseek(fp, 0L, 0);
560 	fprintf(fp, "%d\n", n);
561 	(void) fclose(fp);
562 }
563 
564 /*
565  * Make a temp file name.
566  */
567 char *
568 mktemp(id, num, len)
569 	char	*id;
570 	int	num, len;
571 {
572 	register char *s;
573 
574 	if ((s = malloc(len)) == NULL)
575 		fatal("out of memory");
576 	(void) sprintf(s, "%s/%sA%03d%s", SD, id, num, host);
577 	return(s);
578 }
579