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