xref: /dragonfly/usr.sbin/lpr/lpr/lpr.c (revision 6b5c5d0d)
1 /*
2  * Copyright (c) 1983, 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the University of
22  *	California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  * @(#) Copyright (c) 1983, 1989, 1993 The Regents of the University of California.  All rights reserved.
40  * @(#)from: lpr.c	8.4 (Berkeley) 4/28/95
41  * $FreeBSD: src/usr.sbin/lpr/lpr/lpr.c,v 1.32.2.11 2002/04/28 23:40:23 gad Exp $
42  * $DragonFly: src/usr.sbin/lpr/lpr/lpr.c,v 1.5 2005/08/08 18:58:56 joerg Exp $
43  */
44 
45 /*
46  *      lpr -- off line print
47  *
48  * Allows multiple printers and printers on remote machines by
49  * using information from a printer data base.
50  */
51 
52 #include <sys/param.h>
53 #include <sys/stat.h>
54 
55 #include <netinet/in.h>		/* N_BADMAG uses ntohl() */
56 
57 #include <dirent.h>
58 #include <fcntl.h>
59 #include <a.out.h>
60 #include <err.h>
61 #include <inttypes.h>
62 #include <locale.h>
63 #include <signal.h>
64 #include <syslog.h>
65 #include <pwd.h>
66 #include <grp.h>
67 #include <unistd.h>
68 #include <stdlib.h>
69 #include <stdio.h>
70 #include <ctype.h>
71 #include <string.h>
72 #include "lp.h"
73 #include "lp.local.h"
74 #include "pathnames.h"
75 
76 static char	*cfname;	/* daemon control files, linked from tf's */
77 static char	*class = local_host;	/* class title on header page */
78 static char	*dfname;	/* data files */
79 static char	*fonts[4];	/* troff font names */
80 static char	 format = 'f';	/* format char for printing files */
81 static int	 hdr = 1;	/* print header or not (default is yes) */
82 static int	 iflag;		/* indentation wanted */
83 static int	 inchar;	/* location to increment char in file names */
84 static int	 indent;	/* amount to indent */
85 static const char *jobname;	/* job name on header page */
86 static int	 mailflg;	/* send mail */
87 static int	 nact;		/* number of jobs to act on */
88 static int	 ncopies = 1;	/* # of copies to make */
89 static char	*lpr_username;  /* person sending the print job(s) */
90 static int	 qflag;		/* q job, but don't exec daemon */
91 static int	 rflag;		/* remove files upon completion */
92 static int	 sflag;		/* symbolic link flag */
93 static int	 tfd;		/* control file descriptor */
94 static char	*tfname;	/* tmp copy of cf before linking */
95 static char	*title;		/* pr'ing title */
96 static char     *locale;        /* pr'ing locale */
97 static int	 userid;	/* user id */
98 static char	*Uflag;		/* user name specified with -U flag */
99 static char	*width;		/* width for versatec printing */
100 static char	*Zflag;		/* extra filter options for LPRng servers */
101 
102 static struct stat statb;
103 
104 static void	 card(int _c, const char *_p2);
105 static int	 checkwriteperm(const char *_file, const char *_directory);
106 static void	 chkprinter(const char *_ptrname, struct printer *_pp);
107 static void	 cleanup(int _signo);
108 static void	 copy(const struct printer *_pp, int _f, const char _n[]);
109 static char	*itoa(int _i);
110 static const char  *linked(const char *_file);
111 int		 main(int _argc, char *_argv[]);
112 static char	*lmktemp(const struct printer *_pp, const char *_id,
113 		    int _num, int len);
114 static void	 mktemps(const struct printer *_pp);
115 static int	 nfile(char *_n);
116 static int	 test(const char *_file);
117 static void	 usage(void);
118 
119 uid_t	uid, euid;
120 
121 int
122 main(int argc, char *argv[])
123 {
124 	struct passwd *pw;
125 	struct group *gptr;
126 	const char *arg, *cp, *printer;
127 	char *p;
128 	char buf[BUFSIZ];
129 	int c, i, f, errs;
130 	int	 ret, didlink;
131 	struct stat stb;
132 	struct stat statb1, statb2;
133 	struct printer myprinter, *pp = &myprinter;
134 
135 	printer = NULL;
136 	euid = geteuid();
137 	uid = getuid();
138 	seteuid(uid);
139 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
140 		signal(SIGHUP, cleanup);
141 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
142 		signal(SIGINT, cleanup);
143 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
144 		signal(SIGQUIT, cleanup);
145 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
146 		signal(SIGTERM, cleanup);
147 
148 	progname = argv[0];
149 	gethostname(local_host, sizeof(local_host));
150 	openlog("lpd", 0, LOG_LPR);
151 
152 	errs = 0;
153 	while ((c = getopt(argc, argv,
154 			   ":#:1:2:3:4:C:J:L:P:T:U:Z:cdfghi:lnmprstvw:"))
155 	       != -1)
156 		switch (c) {
157 		case '#':		/* n copies */
158 			i = strtol(optarg, &p, 10);
159 			if (*p)
160 				errx(1, "Bad argument to -#, number expected");
161 			if (i > 0)
162 				ncopies = i;
163 			break;
164 
165 		case '1':		/* troff fonts */
166 		case '2':
167 		case '3':
168 		case '4':
169 			fonts[optopt - '1'] = optarg;
170 			break;
171 
172 		case 'C':		/* classification spec */
173 			hdr++;
174 			class = optarg;
175 			break;
176 
177 		case 'J':		/* job name */
178 			hdr++;
179 			jobname = optarg;
180 			break;
181 
182 		case 'P':		/* specifiy printer name */
183 			printer = optarg;
184 			break;
185 
186 		case 'L':               /* pr's locale */
187 			locale = optarg;
188 			break;
189 
190 		case 'T':		/* pr's title line */
191 			title = optarg;
192 			break;
193 
194 		case 'U':		/* user name */
195 			hdr++;
196 			Uflag = optarg;
197 			break;
198 
199 		case 'Z':
200 			Zflag = optarg;
201 			break;
202 
203 		case 'c':		/* print cifplot output */
204 		case 'd':		/* print tex output (dvi files) */
205 		case 'g':		/* print graph(1G) output */
206 		case 'l':		/* literal output */
207 		case 'n':		/* print ditroff output */
208 		case 't':		/* print troff output (cat files) */
209 		case 'p':		/* print using ``pr'' */
210 		case 'v':		/* print vplot output */
211 			format = optopt;
212 			break;
213 
214 		case 'f':		/* print fortran output */
215 			format = 'r';
216 			break;
217 
218 		case 'h':		/* nulifiy header page */
219 			hdr = 0;
220 			break;
221 
222 		case 'i':		/* indent output */
223 			iflag++;
224 			indent = strtol(optarg, &p, 10);
225 			if (*p)
226 				errx(1, "Bad argument to -i, number expected");
227 			break;
228 
229 		case 'm':		/* send mail when done */
230 			mailflg++;
231 			break;
232 
233 		case 'q':		/* just queue job */
234 			qflag++;
235 			break;
236 
237 		case 'r':		/* remove file when done */
238 			rflag++;
239 			break;
240 
241 		case 's':		/* try to link files */
242 			sflag++;
243 			break;
244 
245 		case 'w':		/* versatec page width */
246 			width = optarg;
247 			break;
248 
249 		case ':':		/* catch "missing argument" error */
250 			if (optopt == 'i') {
251 				iflag++; /* -i without args is valid */
252 				indent = 8;
253 			} else
254 				errs++;
255 			break;
256 
257 		default:
258 			errs++;
259 		}
260 	argc -= optind;
261 	argv += optind;
262 	if (errs)
263 		usage();
264 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
265 		printer = DEFLP;
266 	chkprinter(printer, pp);
267 	if (pp->no_copies && ncopies > 1)
268 		errx(1, "multiple copies are not allowed");
269 	if (pp->max_copies > 0 && ncopies > pp->max_copies)
270 		errx(1, "only %ld copies are allowed", pp->max_copies);
271 	/*
272 	 * Get the identity of the person doing the lpr using the same
273 	 * algorithm as lprm.  Actually, not quite -- lprm will override
274 	 * the login name with "root" if the user is running as root;
275 	 * the daemon actually checks for the string "root" in its
276 	 * permission checking.  Sigh.
277 	 */
278 	userid = getuid();
279 	if (Uflag) {
280 		if (userid != 0 && userid != pp->daemon_user)
281 			errx(1, "only privileged users may use the `-U' flag");
282 		lpr_username = Uflag;		/* -U person doing 'lpr' */
283 	} else {
284 		lpr_username = getlogin();	/* person doing 'lpr' */
285 		if (userid != pp->daemon_user || lpr_username == 0) {
286 			if ((pw = getpwuid(userid)) == NULL)
287 				errx(1, "Who are you?");
288 			lpr_username = pw->pw_name;
289 		}
290 	}
291 
292 	/*
293 	 * Check for restricted group access.
294 	 */
295 	if (pp->restrict_grp != NULL && userid != pp->daemon_user) {
296 		if ((gptr = getgrnam(pp->restrict_grp)) == NULL)
297 			errx(1, "Restricted group specified incorrectly");
298 		if (gptr->gr_gid != getgid()) {
299 			while (*gptr->gr_mem != NULL) {
300 				if ((strcmp(lpr_username, *gptr->gr_mem)) == 0)
301 					break;
302 				gptr->gr_mem++;
303 			}
304 			if (*gptr->gr_mem == NULL)
305 				errx(1, "Not a member of the restricted group");
306 		}
307 	}
308 	/*
309 	 * Check to make sure queuing is enabled if userid is not root.
310 	 */
311 	lock_file_name(pp, buf, sizeof buf);
312 	if (userid && stat(buf, &stb) == 0 && (stb.st_mode & LFM_QUEUE_DIS))
313 		errx(1, "Printer queue is disabled");
314 	/*
315 	 * Initialize the control file.
316 	 */
317 	mktemps(pp);
318 	tfd = nfile(tfname);
319 	seteuid(euid);
320 	fchown(tfd, pp->daemon_user, -1);
321 	/* owned by daemon for protection */
322 	seteuid(uid);
323 	card('H', local_host);
324 	card('P', lpr_username);
325 	card('C', class);
326 	if (hdr && !pp->no_header) {
327 		if (jobname == NULL) {
328 			if (argc == 0)
329 				jobname = "stdin";
330 			else
331 				jobname = ((arg = strrchr(argv[0], '/'))
332 					   ? arg + 1 : argv[0]);
333 		}
334 		card('J', jobname);
335 		card('L', lpr_username);
336 	}
337 	if (format != 'p' && Zflag != 0)
338 		card('Z', Zflag);
339 	if (iflag)
340 		card('I', itoa(indent));
341 	if (mailflg)
342 		card('M', lpr_username);
343 	if (format == 't' || format == 'n' || format == 'd')
344 		for (i = 0; i < 4; i++)
345 			if (fonts[i] != NULL)
346 				card('1'+i, fonts[i]);
347 	if (width != NULL)
348 		card('W', width);
349 	/*
350 	 * XXX
351 	 * Our use of `Z' here is incompatible with LPRng's
352 	 * use.  We assume that the only use of our existing
353 	 * `Z' card is as shown for `p' format (pr) files.
354 	 */
355 	if (format == 'p') {
356 		char *s;
357 
358 		if (locale)
359 			card('Z', locale);
360 		else if ((s = setlocale(LC_TIME, "")) != NULL)
361 			card('Z', s);
362 	}
363 
364 	/*
365 	 * Read the files and spool them.
366 	 */
367 	if (argc == 0)
368 		copy(pp, 0, " ");
369 	else while (argc--) {
370 		if (argv[0][0] == '-' && argv[0][1] == '\0') {
371 			/* use stdin */
372 			copy(pp, 0, " ");
373 			argv++;
374 			continue;
375 		}
376 		if ((f = test(arg = *argv++)) < 0)
377 			continue;	/* file unreasonable */
378 
379 		if (sflag && (cp = linked(arg)) != NULL) {
380 			snprintf(buf, sizeof(buf), "%d %"PRId64, statb.st_dev,
381 				 (uint64_t)statb.st_ino);
382 			card('S', buf);
383 			if (format == 'p')
384 				card('T', title ? title : arg);
385 			for (i = 0; i < ncopies; i++)
386 				card(format, &dfname[inchar-2]);
387 			card('U', &dfname[inchar-2]);
388 			if (f)
389 				card('U', cp);
390 			card('N', arg);
391 			dfname[inchar]++;
392 			nact++;
393 			continue;
394 		}
395 		if (sflag)
396 			printf("%s: %s: not linked, copying instead\n",
397 			    progname, arg);
398 
399 		if (f) {
400 			/*
401 			 * The user wants the file removed after it is copied
402 			 * to the spool area, so see if the file can be moved
403 			 * instead of copy/unlink'ed.  This is much faster and
404 			 * uses less spool space than copying the file.  This
405 			 * can be very significant when running services like
406 			 * samba, pcnfs, CAP, et al.
407 			 */
408 			seteuid(euid);
409 			didlink = 0;
410 			/*
411 			 * There are several things to check to avoid any
412 			 * security issues.  Some of these are redundant
413 			 * under BSD's, but are necessary when lpr is built
414 			 * under some other OS's (which I do do...)
415 			 */
416 			if (lstat(arg, &statb1) < 0)
417 				goto nohardlink;
418 			if (S_ISLNK(statb1.st_mode))
419 				goto nohardlink;
420 			if (link(arg, dfname) != 0)
421 				goto nohardlink;
422 			didlink = 1;
423 			/*
424 			 * Make sure the user hasn't tried to trick us via
425 			 * any race conditions
426 			 */
427 			if (lstat(dfname, &statb2) < 0)
428 				goto nohardlink;
429 			if (statb1.st_dev != statb2.st_dev)
430 				goto nohardlink;
431 			if (statb1.st_ino != statb2.st_ino)
432 				goto nohardlink;
433 			/*
434 			 * Skip if the file already had multiple hard links,
435 			 * because changing the owner and access-bits would
436 			 * change ALL versions of the file
437 			 */
438 			if (statb2.st_nlink > 2)
439 				goto nohardlink;
440 			/*
441 			 * If we can access and remove the original file
442 			 * without special setuid-ness then this method is
443 			 * safe.  Otherwise, abandon the move and fall back
444 			 * to the (usual) copy method.
445 			 */
446 			seteuid(uid);
447 			ret = access(dfname, R_OK);
448 			if (ret == 0)
449 				ret = unlink(arg);
450 			seteuid(euid);
451 			if (ret != 0)
452 				goto nohardlink;
453 			/*
454 			 * Unlink of user file was successful.  Change the
455 			 * owner and permissions, add entries to the control
456 			 * file, and skip the file copying step.
457 			 */
458 			chown(dfname, pp->daemon_user, getegid());
459 			chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
460 			seteuid(uid);
461 			if (format == 'p')
462 				card('T', title ? title : arg);
463 			for (i = 0; i < ncopies; i++)
464 				card(format, &dfname[inchar-2]);
465 			card('U', &dfname[inchar-2]);
466 			card('N', arg);
467 			nact++;
468 			continue;
469 		nohardlink:
470 			if (didlink)
471 				unlink(dfname);
472 			seteuid(uid);           /* restore old uid */
473 		} /* end: if (f) */
474 
475 		if ((i = open(arg, O_RDONLY)) < 0) {
476 			printf("%s: cannot open %s\n", progname, arg);
477 		} else {
478 			copy(pp, i, arg);
479 			close(i);
480 			if (f && unlink(arg) < 0)
481 				printf("%s: %s: not removed\n", progname, arg);
482 		}
483 	}
484 
485 	if (nact) {
486 		close(tfd);
487 		tfname[inchar]--;
488 		/*
489 		 * Touch the control file to fix position in the queue.
490 		 */
491 		seteuid(euid);
492 		if ((tfd = open(tfname, O_RDWR)) >= 0) {
493 			char touch_c;
494 
495 			if (read(tfd, &touch_c, 1) == 1 &&
496 			    lseek(tfd, (off_t)0, 0) == 0 &&
497 			    write(tfd, &touch_c, 1) != 1) {
498 				printf("%s: cannot touch %s\n", progname,
499 				    tfname);
500 				tfname[inchar]++;
501 				cleanup(0);
502 			}
503 			close(tfd);
504 		}
505 		if (link(tfname, cfname) < 0) {
506 			printf("%s: cannot rename %s\n", progname, cfname);
507 			tfname[inchar]++;
508 			cleanup(0);
509 		}
510 		unlink(tfname);
511 		seteuid(uid);
512 		if (qflag)		/* just q things up */
513 			exit(0);
514 		if (!startdaemon(pp))
515 			printf("jobs queued, but cannot start daemon.\n");
516 		exit(0);
517 	}
518 	cleanup(0);
519 	return (1);
520 	/* NOTREACHED */
521 }
522 
523 /*
524  * Create the file n and copy from file descriptor f.
525  */
526 static void
527 copy(const struct printer *pp, int f, const char n[])
528 {
529 	int fd, i, nr, nc;
530 	char buf[BUFSIZ];
531 
532 	if (format == 'p')
533 		card('T', title ? title : n);
534 	for (i = 0; i < ncopies; i++)
535 		card(format, &dfname[inchar-2]);
536 	card('U', &dfname[inchar-2]);
537 	card('N', n);
538 	fd = nfile(dfname);
539 	nr = nc = 0;
540 	while ((i = read(f, buf, BUFSIZ)) > 0) {
541 		if (write(fd, buf, i) != i) {
542 			printf("%s: %s: temp file write error\n", progname, n);
543 			break;
544 		}
545 		nc += i;
546 		if (nc >= BUFSIZ) {
547 			nc -= BUFSIZ;
548 			nr++;
549 			if (pp->max_blocks > 0 && nr > pp->max_blocks) {
550 				printf("%s: %s: copy file is too large\n",
551 				    progname, n);
552 				break;
553 			}
554 		}
555 	}
556 	close(fd);
557 	if (nc==0 && nr==0)
558 		printf("%s: %s: empty input file\n", progname,
559 		    f ? n : "stdin");
560 	else
561 		nact++;
562 }
563 
564 /*
565  * Try and link the file to dfname. Return a pointer to the full
566  * path name if successful.
567  */
568 static const char *
569 linked(const char *file)
570 {
571 	char *cp;
572 	static char buf[MAXPATHLEN];
573 	int ret;
574 
575 	if (*file != '/') {
576 		if (getcwd(buf, sizeof(buf)) == NULL)
577 			return(NULL);
578 		while (file[0] == '.') {
579 			switch (file[1]) {
580 			case '/':
581 				file += 2;
582 				continue;
583 			case '.':
584 				if (file[2] == '/') {
585 					if ((cp = strrchr(buf, '/')) != NULL)
586 						*cp = '\0';
587 					file += 3;
588 					continue;
589 				}
590 			}
591 			break;
592 		}
593 		strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
594 		strncat(buf, file, sizeof(buf) - strlen(buf) - 1);
595 		file = buf;
596 	}
597 	seteuid(euid);
598 	ret = symlink(file, dfname);
599 	seteuid(uid);
600 	return(ret ? NULL : file);
601 }
602 
603 /*
604  * Put a line into the control file.
605  */
606 static void
607 card(int c, const char *p2)
608 {
609 	char buf[BUFSIZ];
610 	char *p1 = buf;
611 	size_t len = 2;
612 
613 	*p1++ = c;
614 	while ((c = *p2++) != '\0' && len < sizeof(buf)) {
615 		*p1++ = (c == '\n') ? ' ' : c;
616 		len++;
617 	}
618 	*p1++ = '\n';
619 	write(tfd, buf, len);
620 }
621 
622 /*
623  * Create a new file in the spool directory.
624  */
625 static int
626 nfile(char *n)
627 {
628 	int f;
629 	int oldumask = umask(0);		/* should block signals */
630 
631 	seteuid(euid);
632 	f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
633 	umask(oldumask);
634 	if (f < 0) {
635 		printf("%s: cannot create %s\n", progname, n);
636 		cleanup(0);
637 	}
638 	if (fchown(f, userid, -1) < 0) {
639 		printf("%s: cannot chown %s\n", progname, n);
640 		cleanup(0);	/* cleanup does exit */
641 	}
642 	seteuid(uid);
643 	if (++n[inchar] > 'z') {
644 		if (++n[inchar-2] == 't') {
645 			printf("too many files - break up the job\n");
646 			cleanup(0);
647 		}
648 		n[inchar] = 'A';
649 	} else if (n[inchar] == '[')
650 		n[inchar] = 'a';
651 	return(f);
652 }
653 
654 /*
655  * Cleanup after interrupts and errors.
656  */
657 static void
658 cleanup(int signo __unused)
659 {
660 	int i;
661 
662 	signal(SIGHUP, SIG_IGN);
663 	signal(SIGINT, SIG_IGN);
664 	signal(SIGQUIT, SIG_IGN);
665 	signal(SIGTERM, SIG_IGN);
666 	i = inchar;
667 	seteuid(euid);
668 	if (tfname)
669 		do
670 			unlink(tfname);
671 		while (tfname[i]-- != 'A');
672 	if (cfname)
673 		do
674 			unlink(cfname);
675 		while (cfname[i]-- != 'A');
676 	if (dfname)
677 		do {
678 			do
679 				unlink(dfname);
680 			while (dfname[i]-- != 'A');
681 			dfname[i] = 'z';
682 		} while (dfname[i-2]-- != 'd');
683 	exit(1);
684 }
685 
686 /*
687  * Test to see if this is a printable file.
688  * Return -1 if it is not, 0 if its printable, and 1 if
689  * we should remove it after printing.
690  */
691 static int
692 test(const char *file)
693 {
694 	struct exec execb;
695 	size_t dlen;
696 	int fd;
697 	char *cp, *dirpath;
698 
699 	if (access(file, 4) < 0) {
700 		printf("%s: cannot access %s\n", progname, file);
701 		return(-1);
702 	}
703 	if (stat(file, &statb) < 0) {
704 		printf("%s: cannot stat %s\n", progname, file);
705 		return(-1);
706 	}
707 	if ((statb.st_mode & S_IFMT) == S_IFDIR) {
708 		printf("%s: %s is a directory\n", progname, file);
709 		return(-1);
710 	}
711 	if (statb.st_size == 0) {
712 		printf("%s: %s is an empty file\n", progname, file);
713 		return(-1);
714  	}
715 	if ((fd = open(file, O_RDONLY)) < 0) {
716 		printf("%s: cannot open %s\n", progname, file);
717 		return(-1);
718 	}
719 	/*
720 	 * XXX Shall we add a similar test for ELF?
721 	 */
722 	if (read(fd, &execb, sizeof(execb)) == sizeof(execb) &&
723 	    !N_BADMAG(execb)) {
724 		printf("%s: %s is an executable program", progname, file);
725 		goto error1;
726 	}
727 	close(fd);
728 	if (rflag) {
729 		/*
730 		 * aside: note that 'cp' is technically a 'const char *'
731 		 * (because it points into 'file'), even though strrchr
732 		 * returns a value of type 'char *'.
733 		 */
734 		if ((cp = strrchr(file, '/')) == NULL) {
735 			if (checkwriteperm(file,".") == 0)
736 				return(1);
737 		} else {
738 			if (cp == file) {
739 				fd = checkwriteperm(file,"/");
740 			} else {
741 				/* strlcpy will change the '/' to '\0' */
742 				dlen = cp - file + 1;
743 				dirpath = malloc(dlen);
744 				strlcpy(dirpath, file, dlen);
745 				fd = checkwriteperm(file, dirpath);
746 				free(dirpath);
747 			}
748 			if (fd == 0)
749 				return(1);
750 		}
751 		printf("%s: %s: is not removable by you\n", progname, file);
752 	}
753 	return(0);
754 
755 error1:
756 	printf(" and is unprintable\n");
757 	close(fd);
758 	return(-1);
759 }
760 
761 static int
762 checkwriteperm(const char *file, const char *directory)
763 {
764 	struct	stat	stats;
765 	if (access(directory, W_OK) == 0) {
766 		stat(directory, &stats);
767 		if (stats.st_mode & S_ISVTX) {
768 			stat(file, &stats);
769 			if(stats.st_uid == userid) {
770 				return(0);
771 			}
772 		} else return(0);
773 	}
774 	return(-1);
775 }
776 
777 /*
778  * itoa - integer to string conversion
779  */
780 static char *
781 itoa(int i)
782 {
783 	static char b[10] = "########";
784 	char *p;
785 
786 	p = &b[8];
787 	do
788 		*p-- = i%10 + '0';
789 	while (i /= 10);
790 	return(++p);
791 }
792 
793 /*
794  * Perform lookup for printer name or abbreviation --
795  */
796 static void
797 chkprinter(const char *ptrname, struct printer *pp)
798 {
799 	int status;
800 
801 	init_printer(pp);
802 	status = getprintcap(ptrname, pp);
803 	switch(status) {
804 	case PCAPERR_OSERR:
805 	case PCAPERR_TCLOOP:
806 		errx(1, "%s: %s", ptrname, pcaperr(status));
807 	case PCAPERR_NOTFOUND:
808 		errx(1, "%s: unknown printer", ptrname);
809 	case PCAPERR_TCOPEN:
810 		warnx("%s: unresolved tc= reference(s)", ptrname);
811 	}
812 }
813 
814 /*
815  * Tell the user what we wanna get.
816  */
817 static void
818 usage(void)
819 {
820 	fprintf(stderr, "%s\n",
821 "usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]\n"
822 	"\t[-Z daemon-options] [-i[numcols]] [-i[numcols]] [-1234 font]\n"
823 	"\t[-L locale] [-wnum] [-cdfghlnmprstv] [name ...]");
824 	exit(1);
825 }
826 
827 
828 /*
829  * Make the temp files.
830  */
831 static void
832 mktemps(const struct printer *pp)
833 {
834 	int len, fd, n;
835 	char *cp;
836 	char buf[BUFSIZ];
837 
838 	snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir);
839 	seteuid(euid);
840 	if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
841 		printf("%s: cannot create %s\n", progname, buf);
842 		exit(1);
843 	}
844 	if (flock(fd, LOCK_EX)) {
845 		printf("%s: cannot lock %s\n", progname, buf);
846 		exit(1);
847 	}
848 	seteuid(uid);
849 	n = 0;
850 	if ((len = read(fd, buf, sizeof(buf))) > 0) {
851 		for (cp = buf; len--; ) {
852 			if (*cp < '0' || *cp > '9')
853 				break;
854 			n = n * 10 + (*cp++ - '0');
855 		}
856 	}
857 	len = strlen(pp->spool_dir) + strlen(local_host) + 8;
858 	tfname = lmktemp(pp, "tf", n, len);
859 	cfname = lmktemp(pp, "cf", n, len);
860 	dfname = lmktemp(pp, "df", n, len);
861 	inchar = strlen(pp->spool_dir) + 3;
862 	n = (n + 1) % 1000;
863 	lseek(fd, (off_t)0, 0);
864 	snprintf(buf, sizeof(buf), "%03d\n", n);
865 	write(fd, buf, strlen(buf));
866 	close(fd);	/* unlocks as well */
867 }
868 
869 /*
870  * Make a temp file name.
871  */
872 static char *
873 lmktemp(const struct printer *pp, const char *id, int num, int len)
874 {
875 	char *s;
876 
877 	if ((s = malloc(len)) == NULL)
878 		errx(1, "out of memory");
879 	snprintf(s, len, "%s/%sA%03d%s", pp->spool_dir, id, num, local_host);
880 	return(s);
881 }
882