Home | History | Annotate | Line # | Download | only in lpr
      1 /*	$NetBSD: lpr.c,v 1.47 2023/06/24 05:15:42 msaitoh Exp $	*/
      2 
      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. Neither the name of the University nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  */
     37 
     38 #include <sys/cdefs.h>
     39 #ifndef lint
     40 __COPYRIGHT("@(#) Copyright (c) 1983, 1989, 1993\
     41  The Regents of the University of California.  All rights reserved.");
     42 #if 0
     43 static char sccsid[] = "@(#)lpr.c	8.4 (Berkeley) 4/28/95";
     44 #else
     45 __RCSID("$NetBSD: lpr.c,v 1.47 2023/06/24 05:15:42 msaitoh Exp $");
     46 #endif
     47 #endif /* not lint */
     48 
     49 /*
     50  *      lpr -- off line print
     51  *
     52  * Allows multiple printers and printers on remote machines by
     53  * using information from a printer data base.
     54  */
     55 
     56 #include <sys/param.h>
     57 #include <sys/stat.h>
     58 #include <sys/file.h>
     59 
     60 #include <dirent.h>
     61 #include <fcntl.h>
     62 #include <signal.h>
     63 #include <syslog.h>
     64 #include <pwd.h>
     65 #include <grp.h>
     66 #include <unistd.h>
     67 #include <stdlib.h>
     68 #include <stdio.h>
     69 #include <ctype.h>
     70 #include <string.h>
     71 #include <errno.h>
     72 #include <err.h>
     73 
     74 #include "lp.h"
     75 #include "lp.local.h"
     76 #include "pathnames.h"
     77 
     78 static char	*cfname;	/* daemon control files, linked from tf's */
     79 static char	*class = host;	/* class title on header page */
     80 static char	*dfname;	/* data files */
     81 static char	*fonts[4];	/* troff font names */
     82 static char	 format = 'f';	/* format char for printing files */
     83 static int	 hdr = 1;	/* print header or not (default is yes) */
     84 static int	 iflag;		/* indentation wanted */
     85 static int	 inchar;	/* location to increment char in file names */
     86 static int	 indent;	/* amount to indent */
     87 static const char *jobname;	/* job name on header page */
     88 static int	 mailflg;	/* send mail */
     89 static int	 nact;		/* number of jobs to act on */
     90 static int	 ncopies = 1;	/* # of copies to make */
     91 static const char *person;	/* user name */
     92 static int	 qflag;		/* q job, but don't exec daemon */
     93 static int	 reqid;		/* request id */
     94 static int	 rflag;		/* remove files upon completion */
     95 static int	 Rflag;		/* print request id - like POSIX lp */
     96 static int	 sflag;		/* symbolic link flag */
     97 static int	 tfd;		/* control file descriptor */
     98 static char	*tfname;	/* tmp copy of cf before linking */
     99 static char	*title;		/* pr'ing title */
    100 static int	 userid;	/* user id */
    101 static char	*width;		/* width for versatec printing */
    102 
    103 static struct stat statb;
    104 
    105 static void	 card(int, const char *);
    106 static void	 chkprinter(const char *);
    107 static void	 cleanup(int) __dead;
    108 static void	 copy(int, const char *);
    109 static char	*itoa(int);
    110 static const char	*linked(const char *);
    111 static char	*lmktemp(const char *, int, int);
    112 static void	 mktemps(void);
    113 static int	 nfile(char *);
    114 static int	 test(const char *);
    115 static void	 usage(void) __dead;
    116 
    117 uid_t	uid, euid;
    118 
    119 int
    120 main(int argc, char *argv[])
    121 {
    122 	struct passwd *pw;
    123 	struct group *gptr;
    124 	char *arg;
    125 	const char *cp;
    126 	char buf[MAXPATHLEN];
    127 	int i, f, errs, c;
    128 	struct stat stb;
    129 	int oerrno;
    130 
    131 	euid = geteuid();
    132 	uid = getuid();
    133 	seteuid(uid);
    134 
    135 	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
    136 		signal(SIGHUP, cleanup);
    137 	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
    138 		signal(SIGINT, cleanup);
    139 	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
    140 		signal(SIGQUIT, cleanup);
    141 	if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
    142 		signal(SIGTERM, cleanup);
    143 
    144 	setprogname(*argv);
    145 	gethostname(host, sizeof (host));
    146 	host[sizeof(host) - 1] = '\0';
    147 	openlog("lpd", 0, LOG_LPR);
    148 
    149 	errs = 0;
    150 	while ((c = getopt(argc, argv,
    151 	    ":#:1:2:3:4:C:J:P:RT:U:cdfghi:lmnopqrstvw:")) != -1) {
    152 		switch (c) {
    153 
    154 		case '#':		/* n copies */
    155 			if (isdigit((unsigned char)*optarg)) {
    156 				i = atoi(optarg);
    157 				if (i > 0)
    158 					ncopies = i;
    159 			}
    160 			break;
    161 
    162 		case '4':		/* troff fonts */
    163 		case '3':
    164 		case '2':
    165 		case '1':
    166 			fonts[optopt - '1'] = optarg;
    167 			break;
    168 
    169 		case 'C':		/* classification spec */
    170 			hdr++;
    171 			class = optarg;
    172 			break;
    173 
    174 		case 'J':		/* job name */
    175 			hdr++;
    176 			jobname = optarg;
    177 			break;
    178 
    179 		case 'P':		/* specify printer name */
    180 			printer = optarg;
    181 			break;
    182 
    183 		case 'R':		/* print request id */
    184 			Rflag++;
    185 			break;
    186 
    187 		case 'T':		/* pr's title line */
    188 			title = optarg;
    189 			break;
    190 
    191 		case 'U':		/* user name */
    192 			hdr++;
    193 			person = optarg;
    194 			break;
    195 
    196 		case 'c':		/* print cifplot output */
    197 		case 'd':		/* print tex output (dvi files) */
    198 		case 'g':		/* print graph(1G) output */
    199 		case 'l':		/* literal output */
    200 		case 'o':		/* print postscript output */
    201 		case 'n':		/* print ditroff output */
    202 		case 'p':		/* print using ``pr'' */
    203 		case 't':		/* print troff output (cat files) */
    204 		case 'v':		/* print vplot output */
    205 			format = optopt;
    206 			break;
    207 
    208 		case 'f':		/* print fortran output */
    209 			format = 'r';
    210 			break;
    211 
    212 		case 'h':		/* toggle want of header page */
    213 			hdr = !hdr;
    214 			break;
    215 
    216 		case 'i':		/* indent output */
    217 			iflag++;
    218 			indent = atoi(optarg);
    219 			if (indent < 0)
    220 				indent = 8;
    221 			break;
    222 
    223 		case 'm':		/* send mail when done */
    224 			mailflg++;
    225 			break;
    226 
    227 		case 'q':		/* just q job */
    228 			qflag++;
    229 			break;
    230 
    231 		case 'r':		/* remove file when done */
    232 			rflag++;
    233 			break;
    234 
    235 		case 's':		/* try to link files */
    236 			sflag++;
    237 			break;
    238 
    239 		case 'w':		/* versatec page width */
    240 			width = optarg;
    241 			break;
    242 
    243 		case ':':               /* catch "missing argument" error */
    244 			if (optopt == 'i') {
    245 				iflag++; /* -i without args is valid */
    246 				indent = 8;
    247 			} else
    248 				errs++;
    249 			break;
    250 
    251 		default:
    252 			errs++;
    253 		}
    254 	}
    255 	argc -= optind;
    256 	argv += optind;
    257 	if (errs)
    258 		usage();
    259 	if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
    260 		printer = DEFLP;
    261 	chkprinter(printer);
    262 	if (SC && ncopies > 1)
    263 		errx(EXIT_FAILURE, "multiple copies are not allowed");
    264 	if (MC > 0 && ncopies > MC)
    265 		errx(EXIT_FAILURE, "only %ld copies are allowed", MC);
    266 	/*
    267 	 * Get the identity of the person doing the lpr using the same
    268 	 * algorithm as lprm.
    269 	 */
    270 	userid = getuid();
    271 	if (userid != DU || person == 0) {
    272 		if ((pw = getpwuid(userid)) == NULL)
    273 			errx(EXIT_FAILURE, "Who are you?");
    274 		person = pw->pw_name;
    275 	}
    276 	/*
    277 	 * Check for restricted group access.
    278 	 */
    279 	if (RG != NULL && userid != DU) {
    280 		if ((gptr = getgrnam(RG)) == NULL)
    281 			errx(EXIT_FAILURE,
    282 			     "Restricted group specified incorrectly");
    283 		if (gptr->gr_gid != getgid()) {
    284 			while (*gptr->gr_mem != NULL) {
    285 				if ((strcmp(person, *gptr->gr_mem)) == 0)
    286 					break;
    287 				gptr->gr_mem++;
    288 			}
    289 			if (*gptr->gr_mem == NULL)
    290 				errx(EXIT_FAILURE,
    291 				     "Not a member of the restricted group");
    292 		}
    293 	}
    294 	/*
    295 	 * Check to make sure queuing is enabled if userid is not root.
    296 	 */
    297 	(void)snprintf(buf, sizeof buf, "%s/%s", SD, LO);
    298 	if (userid && stat(buf, &stb) == 0 && (stb.st_mode & S_IXGRP))
    299 		errx(EXIT_FAILURE, "Printer queue is disabled");
    300 	/*
    301 	 * Initialize the control file.
    302 	 */
    303 	mktemps();
    304 	tfd = nfile(tfname);
    305 	seteuid(euid);
    306 	(void)fchown(tfd, DU, -1);	/* owned by daemon for protection */
    307 	seteuid(uid);
    308 	card('H', host);
    309 	card('P', person);
    310 	if (hdr && !SH) {
    311 		if (jobname == NULL) {
    312 			if (argc == 0)
    313 				jobname = "stdin";
    314 			else
    315 				jobname = (arg = strrchr(argv[0], '/')) ?
    316 				    arg+1 : argv[0];
    317 		}
    318 		card('J', jobname);
    319 		card('C', class);
    320 		card('L', person);
    321 	}
    322 	if (iflag)
    323 		card('I', itoa(indent));
    324 	if (mailflg)
    325 		card('M', person);
    326 	if (format == 't' || format == 'n' || format == 'd')
    327 		for (i = 0; i < 4; i++)
    328 			if (fonts[i] != NULL)
    329 				card('1'+i, fonts[i]);
    330 	if (width != NULL)
    331 		card('W', width);
    332 
    333 	/*
    334 	 * Read the files and spool them.
    335 	 */
    336 	if (argc == 0)
    337 		copy(0, " ");
    338 	else while (argc--) {
    339 		if (argv[0][0] == '-' && argv[0][1] == '\0') {
    340 			/* use stdin */
    341 			copy(0, " ");
    342 			argv++;
    343 			continue;
    344 		}
    345 		if ((f = test(arg = *argv++)) < 0)
    346 			continue;	/* file unreasonable */
    347 
    348 		if (sflag && (cp = linked(arg)) != NULL) {
    349 			(void)snprintf(buf, sizeof buf,
    350 			    "%llu %llu",
    351 			    (unsigned long long)statb.st_dev,
    352 			    (unsigned long long)statb.st_ino);
    353 			card('S', buf);
    354 			if (format == 'p')
    355 				card('T', title ? title : arg);
    356 			for (i = 0; i < ncopies; i++)
    357 				card(format, &dfname[inchar-2]);
    358 			card('U', &dfname[inchar-2]);
    359 			if (f)
    360 				card('U', cp);
    361 			card('N', arg);
    362 			dfname[inchar]++;
    363 			nact++;
    364 			continue;
    365 		}
    366 		if (sflag)
    367 			warnx("%s: not linked, copying instead", arg);
    368 		seteuid(uid);
    369 		if ((i = open(arg, O_RDONLY)) < 0) {
    370 			oerrno = errno;
    371 			seteuid(uid);
    372 			errno = oerrno;
    373 			warn("cannot open %s", arg);
    374 			continue;
    375 		} else {
    376 			copy(i, arg);
    377 			(void)close(i);
    378 			if (f && unlink(arg) < 0)
    379 				warn("%s: not removed", arg);
    380 		}
    381 		seteuid(uid);
    382 	}
    383 
    384 	if (nact) {
    385 		(void)close(tfd);
    386 		tfname[inchar]--;
    387 		/*
    388 		 * Touch the control file to fix position in the queue.
    389 		 */
    390 		seteuid(euid);
    391 		if ((tfd = open(tfname, O_RDWR)) >= 0) {
    392 			char ch;
    393 
    394 			if (read(tfd, &ch, 1) == 1 &&
    395 			    lseek(tfd, (off_t)0, 0) == 0 &&
    396 			    write(tfd, &ch, 1) != 1) {
    397 				warn("cannot touch %s", tfname);
    398 				tfname[inchar]++;
    399 				cleanup(0);
    400 			}
    401 			(void)close(tfd);
    402 		}
    403 		if (link(tfname, cfname) < 0) {
    404 			warn("cannot rename %s", cfname);
    405 			tfname[inchar]++;
    406 			cleanup(0);
    407 		}
    408 		unlink(tfname);
    409 		seteuid(uid);
    410 		if (Rflag)
    411 			printf("request id is %d\n", reqid);
    412 		if (qflag)		/* just queue things up */
    413 			exit(0);
    414 		if (!startdaemon(printer))
    415 			printf("jobs queued, but cannot start daemon.\n");
    416 		exit(0);
    417 	}
    418 	cleanup(0);
    419 #ifdef __GNUC__
    420 	return (0);
    421 #endif
    422 	/* NOTREACHED */
    423 }
    424 
    425 /*
    426  * Create the file n and copy from file descriptor f.
    427  */
    428 static void
    429 copy(int f, const char *n)
    430 {
    431 	int fd, i, nr;
    432 	size_t nc;
    433 	char buf[BUFSIZ];
    434 
    435 	if (format == 'p')
    436 		card('T', title ? title : n);
    437 	for (i = 0; i < ncopies; i++)
    438 		card(format, &dfname[inchar-2]);
    439 	card('U', &dfname[inchar-2]);
    440 	card('N', n);
    441 	fd = nfile(dfname);
    442 	nr = nc = 0;
    443 	while ((i = read(f, buf, sizeof buf)) > 0) {
    444 		if (write(fd, buf, i) != i) {
    445 			warn("%s: temp file write error", n);
    446 			break;
    447 		}
    448 		nc += i;
    449 		if (nc >= sizeof buf) {
    450 			nc -= sizeof buf;
    451 			nr++;
    452 			if (MX > 0 && nr > MX) {
    453 				warnx("%s: copy file is too large "
    454 				    "(check :mx:?)\n", n);
    455 				break;
    456 			}
    457 		}
    458 	}
    459 	(void)close(fd);
    460 	if (nc == 0 && nr == 0)
    461 		printf("%s: %s: empty input file\n", getprogname(),
    462 		    f ? n : "stdin");
    463 	else
    464 		nact++;
    465 }
    466 
    467 /*
    468  * Try and link the file to dfname. Return a pointer to the full
    469  * path name if successful.
    470  */
    471 static const char *
    472 linked(const char *file)
    473 {
    474 	char *cp;
    475 	static char buf[BUFSIZ];
    476 	int ret;
    477 
    478 	if (*file != '/') {
    479 		/* XXX: 2 and file for "/file" */
    480 		if (getcwd(buf, sizeof(buf) - 2 - strlen(file)) == NULL)
    481 			return(NULL);
    482 		while (file[0] == '.') {
    483 			switch (file[1]) {
    484 			case '/':
    485 				file += 2;
    486 				continue;
    487 			case '.':
    488 				if (file[2] == '/') {
    489 					if ((cp = strrchr(buf, '/')) != NULL)
    490 						*cp = '\0';
    491 					file += 3;
    492 					continue;
    493 				}
    494 			}
    495 			break;
    496 		}
    497 		strlcat(buf, "/", sizeof(buf));
    498 		strlcat(buf, file, sizeof(buf));
    499 		file = buf;
    500 	}
    501 	seteuid(euid);
    502 	ret = symlink(file, dfname);
    503 	seteuid(uid);
    504 	return(ret ? NULL : file);
    505 }
    506 
    507 /*
    508  * Put a line into the control file.
    509  */
    510 static void
    511 card(int c, const char *p2)
    512 {
    513 	char buf[BUFSIZ];
    514 	char *p1 = buf;
    515 	size_t len = 2;
    516 
    517 	if (strlen(p2) > BUFSIZ - 2)
    518 		errx(EXIT_FAILURE,
    519 		     "Internal error:  String longer than %d", BUFSIZ);
    520 
    521 	*p1++ = c;
    522 	while ((c = *p2++) != '\0') {
    523 		*p1++ = (c == '\n') ? ' ' : c;
    524 		len++;
    525 	}
    526 	*p1++ = '\n';
    527 	if (write(tfd, buf, len) != (ssize_t)len)
    528 		warn("Control file write error");
    529 }
    530 
    531 /*
    532  * Create a new file in the spool directory.
    533  */
    534 static int
    535 nfile(char *n)
    536 {
    537 	int f;
    538 	int oldumask = umask(0);		/* should block signals */
    539 	int oerrno;
    540 
    541 	seteuid(euid);
    542 	f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
    543 	oerrno = errno;
    544 	(void)umask(oldumask);
    545 	if (f < 0) {
    546 		errno = oerrno;
    547 		warn("cannot create %s", n);
    548 		cleanup(0);
    549 	}
    550 	if (fchown(f, userid, -1) < 0) {
    551 		warn("cannot chown %s", n);
    552 		cleanup(0);	/* cleanup does exit */
    553 	}
    554 	seteuid(uid);
    555 	if (++n[inchar] > 'z') {
    556 		if (++n[inchar-2] == 't') {
    557 			printf("too many files - break up the job\n");
    558 			cleanup(0);
    559 		}
    560 		n[inchar] = 'A';
    561 	} else if (n[inchar] == '[')
    562 		n[inchar] = 'a';
    563 	return(f);
    564 }
    565 
    566 /*
    567  * Cleanup after interrupts and errors.
    568  */
    569 static void
    570 cleanup(int signo)
    571 {
    572 	int i;
    573 
    574 	signal(SIGHUP, SIG_IGN);
    575 	signal(SIGINT, SIG_IGN);
    576 	signal(SIGQUIT, SIG_IGN);
    577 	signal(SIGTERM, SIG_IGN);
    578 	i = inchar;
    579 	seteuid(euid);
    580 	if (tfname)
    581 		do
    582 			unlink(tfname);
    583 		while (tfname[i]-- != 'A');
    584 	if (cfname)
    585 		do
    586 			unlink(cfname);
    587 		while (cfname[i]-- != 'A');
    588 	if (dfname)
    589 		do {
    590 			do
    591 				unlink(dfname);
    592 			while (dfname[i]-- != 'A');
    593 			dfname[i] = 'z';
    594 		} while (dfname[i-2]-- != 'd');
    595 	exit(1);
    596 }
    597 
    598 /*
    599  * Test to see if this is a printable file.
    600  * Return -1 if it is not, 0 if its printable, and 1 if
    601  * we should remove it after printing.
    602  */
    603 static int
    604 test(const char *file)
    605 {
    606 	int fd;
    607 	char *cp;
    608 
    609 	seteuid(uid);
    610 	if (access(file, 4) < 0) {
    611 		warn("cannot access %s", file);
    612 		goto bad;
    613 	}
    614 	if (stat(file, &statb) < 0) {
    615 		warn("cannot stat %s", file);
    616 		goto bad;
    617 	}
    618 	if (S_ISDIR(statb.st_mode)) {
    619 		warnx("%s is a directory", file);
    620 		goto bad;
    621 	}
    622 	if (statb.st_size == 0) {
    623 		warnx("%s is an empty file", file);
    624 		goto bad;
    625  	}
    626 	if ((fd = open(file, O_RDONLY)) < 0) {
    627 		warn("cannot open %s", file);
    628 		goto bad;
    629 	}
    630 	(void)close(fd);
    631 	if (rflag) {
    632 		if ((cp = strrchr(file, '/')) == NULL) {
    633 			if (access(".", 2) == 0)
    634 				return(1);
    635 		} else {
    636 			if (cp == file) {
    637 				fd = access("/", 2);
    638 			} else {
    639 				*cp = '\0';
    640 				fd = access(file, 2);
    641 				*cp = '/';
    642 			}
    643 			if (fd == 0)
    644 				return(1);
    645 		}
    646 		warnx("%s: is not removable by you", file);
    647 	}
    648 	return(0);
    649 bad:
    650 	seteuid(uid);
    651 	return(-1);
    652 }
    653 
    654 /*
    655  * itoa - integer to string conversion
    656  */
    657 static char *
    658 itoa(int i)
    659 {
    660 	static char b[10] = "########";
    661 	char *p;
    662 
    663 	p = &b[8];
    664 	do
    665 		*p-- = i%10 + '0';
    666 	while (i /= 10);
    667 	return(++p);
    668 }
    669 
    670 /*
    671  * Perform lookup for printer name or abbreviation --
    672  */
    673 static void
    674 chkprinter(const char *s)
    675 {
    676 	char *cp;
    677 
    678 	getprintcap(s);
    679 	RG = cgetstr(bp, "rg", &cp) == -1 ? NULL : cp;
    680 	if (cgetnum(bp, "mx", &MX) < 0)
    681 		MX = DEFMX;
    682 	if (cgetnum(bp,"mc", &MC) < 0)
    683 		MC = DEFMAXCOPIES;
    684 	if (cgetnum(bp, "du", &DU) < 0)
    685 		DU = DEFUID;
    686 	SC = (cgetcap(bp, "sc", ':') != NULL);
    687 }
    688 
    689 /*
    690  * Make the temp files.
    691  */
    692 static void
    693 mktemps(void)
    694 {
    695 	int len, fd, n;
    696 	char *cp;
    697 	char buf[MAXPATHLEN];
    698 
    699 	(void)snprintf(buf, sizeof(buf), "%s/.seq", SD);
    700 	seteuid(euid);
    701 	if ((fd = open(buf, O_RDWR|O_CREAT, 0664)) < 0)
    702 		err(1, "cannot create %s", buf);
    703 	if (flock(fd, LOCK_EX))
    704 		err(1, "cannot lock %s", buf);
    705 	seteuid(uid);
    706 	n = 0;
    707 	if ((len = read(fd, buf, sizeof(buf))) > 0) {
    708 		for (cp = buf; len--; ) {
    709 			if (*cp < '0' || *cp > '9')
    710 				break;
    711 			n = n * 10 + (*cp++ - '0');
    712 		}
    713 	}
    714 	reqid = n;
    715 	len = strlen(SD) + strlen(host) + 8;
    716 	tfname = lmktemp("tf", n, len);
    717 	cfname = lmktemp("cf", n, len);
    718 	dfname = lmktemp("df", n, len);
    719 	inchar = strlen(SD) + 3;
    720 	n = (n + 1) % 1000;
    721 	(void)lseek(fd, (off_t)0, 0);
    722 	(void)snprintf(buf, sizeof(buf), "%03d\n", n);
    723 	(void)write(fd, buf, strlen(buf));
    724 	(void)close(fd);	/* unlocks as well */
    725 }
    726 
    727 /*
    728  * Make a temp file name.
    729  */
    730 static char *
    731 lmktemp(const char *id, int num, int len)
    732 {
    733 	char *s;
    734 
    735 	if ((s = malloc(len)) == NULL)
    736 		err(EXIT_FAILURE, NULL);
    737 	(void)snprintf(s, len, "%s/%sA%03d%s", SD, id, num, host);
    738 	return(s);
    739 }
    740 
    741 static void
    742 usage(void)
    743 {
    744 
    745 	fprintf(stderr,
    746 	    "Usage: %s [-Pprinter] [-#num] [-C class] [-J job] [-T title] "
    747 	    "[-U user]\n"
    748 	    "%s [-i[numcols]] [-1234 font] [-wnum] [-cdfghlmnopqRrstv] "
    749 	    "[name ...]\n", getprogname(), getprogname());
    750 	exit(1);
    751 }
    752