Merge branch 'vendor/LESS'
[dragonfly.git] / usr.sbin / lpr / lpr / lpr.c
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 }