* 1.1 mike 5/77 - all files copied to spooler space
* fix for allowing variable indents (-i flag)
* 1.3 dlm/njl 12 Dec 1977
* fix for open pipe when spawning progess (affects lpd)
* test for data files; add -C option for classification
* and change format of $L card; reversed 1.1 on copies
* add job name and -h option
* -h option changed to -J
* added -H option to cause header page to be
* printed, default is no header page
* added -p option to cause files to be printed
* default for classification changed
* multiple printer logic changed,
* printer number sent to daemon.
* Mods for Version 7, plus portability
* (in preparation for UNIX/24V)
* Mods for protected spooling area, see note below
* Add N card for file names to help out sq
* 3.3 sjl Mar 27 1981 from decvax!shannon
* Mods and cleanup for 4bsd.
* Send mail option added.
* Support multiple printers and daemons through termcap-like
* Check for printer being down (mode 0 on device)
* Clean up handling of printcap database, add more defaults
char lpr_id
[] = "~|^`lpr.c:\t4.2\t1 May 1981\n";
/* lpr.c 4.10 83/03/09 */
* Allows multiple printers and printers on remote machines by
* using information from a printer data base.
char *tfname
; /* tmp copy of cf before linking */
char *cfname
; /* daemon control files, linked from tf's */
char *dfname
; /* data files */
int nact
; /* number of jobs to act on */
int tfd
; /* control file descriptor */
int mailflg
; /* send mail */
int qflag
; /* q job, but don't exec daemon */
char format
= 'f'; /* format char for printing files */
int rflag
; /* remove files upon completion */
int lflag
; /* link flag */
char *person
; /* user name */
int inchar
; /* location to increment char in file names */
int ncopies
= 1; /* # of copies to make */
int iflag
; /* indentation wanted */
int indent
; /* amount to indent */
char *DN
; /* path name to daemon program */
char *LP
; /* line printer device name */
char *RM
; /* remote machine name if no local printer */
char *SD
; /* spool directory */
int MX
; /* maximum size in blocks of a print file */
int hdr
= 1; /* print header or not (default is yes) */
int userid
; /* user id */
char *title
; /* pr'ing title */
char *fonts
[4]; /* troff font names */
char *width
; /* width for versatec printing */
char host
[32]; /* host name */
char *class = host
; /* class title on header page */
char *jobname
; /* job name on header page */
char *name
; /* program name */
extern struct passwd
*getpwuid(), *getpwnam();
* Strategy to maintain protected spooling area:
* 1. Spooling area is writable only by daemon and spooling group
* 2. lpr runs setuid root and setgrp spooling group; it uses
* root to access any file it wants (verifying things before
* with an access call) and group id to know how it should
* set up ownership of files in spooling area.
* 3. Files in spooling area are owned by daemon and spooling
* 4. lpd runs setuid root and setgrp spooling group to
* access files and printer. Users can't get to anything
* w/o help of lpq and lprm programs.
if (signal(SIGHUP
, SIG_IGN
) != SIG_IGN
)
if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
if (signal(SIGQUIT
, SIG_IGN
) != SIG_IGN
)
if (signal(SIGTERM
, SIG_IGN
) != SIG_IGN
)
gethostname(host
, sizeof (host
));
while (argc
> 1 && (arg
= argv
[1])[0] == '-') {
case 'P': /* specifiy printer name */
case 'C': /* classification spec */
case 'T': /* pr's title line */
case 'l': /* literal output */
case 'p': /* print using ``pr'' */
case 't': /* print troff output */
case 'c': /* print cifplot output */
case 'v': /* print vplot output */
case '4': /* troff fonts */
fonts
[arg
[1] - '1'] = *++argv
;
case 'w': /* versatec page width */
case 'r': /* remove file when done */
case 'm': /* send mail when done */
case 'h': /* toggle want of header page */
case 's': /* try to link files */
case 'q': /* just q job */
case 'i': /* indent output */
indent
= arg
[2] ? atoi(&arg
[2]) : 8;
if (printer
== NULL
&& (printer
= getenv("PRINTER")) == NULL
)
if (!chkprinter(printer
)) {
printf("%s: unknown printer %s\n", name
, printer
);
* Get the identity of the person doing the lpr and initialize the
if ((person
= getlogin()) == NULL
|| strlen(person
) == 0) {
if ((pw
= getpwuid(userid
)) == NULL
)
} else if ((pw
= getpwnam(person
)) != NULL
)
userid
= pw
->pw_uid
; /* in case of su */
if ((f
= test(arg
= *++argv
)) < 0)
continue; /* file unreasonable */
if ((f
& 1) && (cp
= linked(arg
)) != NULL
) {
card('T', title
? title
: arg
);
for (i
= 0; i
< ncopies
; i
++)
card(format
, &dfname
[inchar
-2]);
card('U', &dfname
[inchar
-2]);
if ((i
= open(arg
, 0)) < 0) {
printf("%s: cannot open %s\n", name
, arg
);
if ((f
& 2) && unlink(arg
))
printf("%s: cannot remove %s\n", name
, arg
);
if (link(tfname
, cfname
) < 0) {
printf("%s: cannot rename %s\n", name
, cfname
);
if (qflag
) /* just q things up */
if (*LP
&& stat(LP
, &stb
) >= 0 && (stb
.st_mode
& 0777) == 0) {
printf("jobs queued, but line printer is down.\n");
execl(DN
, (arg
= rindex(DN
, '/')) ? arg
+1 : DN
, printer
, 0);
printf("jobs queued, but cannot start daemon.\n");
* Create the file n and copy from file descriptor f.
register int fd
, i
, nr
, nc
;
card('T', title
? title
: n
);
for (i
= 0; i
< ncopies
; i
++)
card(format
, &dfname
[inchar
-2]);
card('U', &dfname
[inchar
-2]);
while ((i
= read(f
, buf
, BUFSIZ
)) > 0) {
if (write(fd
, buf
, i
) != i
) {
printf("%s: %s: temp file write error\n", name
, n
);
printf("%s: %s: copy file is too large\n", name
, n
);
* Try and link the file to dfname. Return a pointer to the full
* path name if successful.
if ((cp
= rindex(buf
, '/')) != NULL
)
return(symlink(file
, dfname
) ? NULL
: file
);
* Put a line into the control file.
while ((c
= *p2
++) != '\0') {
* Create a new file in the spool directory.
int oldumask
= umask(0); /* should block signals */
printf("%s: cannot create %s\n", name
, n
);
if (chown(n
, userid
, -1) < 0) {
if (chown(n
, userid
, getegid()) < 0) {
printf("%s: cannot chown %s\n", name
, n
);
* Cleanup after interrupts and errors.
signal(SIGQUIT
, SIG_IGN
);
signal(SIGTERM
, SIG_IGN
);
while (tfname
[i
] != 'A') {
while (cfname
[i
] != 'A') {
while (dfname
[i
] != 'A') {
* Test to see if this is a printable file.
* Return -1 if it is not, 1 if we should try to link and or in 2 if
* we should remove it after printing.
if (access(file
, 4) < 0) {
printf("%s: cannot access %s\n", name
, file
);
if (stat(file
, &statb
) < 0) {
printf("%s: cannot stat %s\n", name
, file
);
if ((statb
.st_mode
& S_IFMT
) == S_IFDIR
) {
printf("%s: %s is a directory\n", name
, file
);
if ((fd
= open(file
, 0)) < 0) {
printf("%s: cannot open %s\n", name
, file
);
if (read(fd
, &execb
, sizeof(execb
)) == sizeof(execb
))
printf("%s: %s is an executable program", name
, file
);
printf("%s: %s is an archive file", name
, file
);
if (lflag
&& (statb
.st_mode
& 04))
if ((cp
= rindex(file
, '/')) == NULL
) {
if (access(file
, 2) == 0)
printf(" and is unprintable\n");
* itoa - integer to string conversion
static char b
[10] = "########";
* Perform lookup for printer name or abbreviation --
static char buf
[BUFSIZ
/2];
if ((stat
= pgetent(b
, s
)) < 0) {
printf("%s: can't open printer description file\n", name
);
if ((DN
= pgetstr("dn", &bp
)) == NULL
)
if ((LP
= pgetstr("lp", &bp
)) == NULL
)
if ((SD
= pgetstr("sd", &bp
)) == NULL
)
if ((MX
= pgetnum("mx")) < 0)
char buf
[BUFSIZ
], *mktemp();
(void) sprintf(buf
, "%s/.seq", SD
);
if ((fp
= fopen(buf
, "r+")) == NULL
) {
if ((fp
= fopen(buf
, "w")) == NULL
) {
printf("%s: cannot create %s\n", name
, buf
);
if (flock(fileno(fp
), FEXLOCK
)) {
printf("%s: cannot lock %s\n", name
, buf
);
while ((c
= getc(fp
)) >= '0' && c
<= '9')
len
= strlen(SD
) + strlen(host
) + 8;
tfname
= mktemp("tf", n
, len
);
cfname
= mktemp("cf", n
, len
);
dfname
= mktemp("df", n
, len
);
if ((s
= malloc(len
)) == NULL
) {
printf("%s: out of memory\n", name
);
(void) sprintf(s
, "%s/%sA%03d%s", SD
, id
, num
, host
);