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