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