1 /* lpd 1.6 - Printer daemon Author: Kees J. Bot
10 #include <sys/types.h>
17 char PRINTER
[] = "/dev/lp";
18 char SPOOL
[] = "/usr/spool/lpd";
19 char LOG
[] = "/dev/log";
21 void report(char *mess
)
23 fprintf(stderr
, "lpd: %s: %s\n", mess
, strerror(errno
));
26 void fatal(char *mess
)
32 char jobX
[] = "jobXXXXXX";
33 char tmpX
[] = "tmpXXXXXX";
35 void spoolerr(char *file
)
44 void spool(char *path
)
45 /* Place a file into the spool directory, either by copying it, or by leaving
58 if ((f
= open(path
, O_RDONLY
)) >= 0) {
67 if ((t
= fopen(tmpX
, "w")) == nil
) spoolerr(tmpX
);
69 while ((c
= getchar()) != EOF
&& putc(c
, t
) != EOF
) {}
71 if (ferror(stdin
)) spoolerr(path
);
73 if (ferror(t
) || fclose(t
) == EOF
) spoolerr(tmpX
);
78 if ((j
= open(jobX
, O_WRONLY
|O_CREAT
|O_EXCL
, 0000)) < 0) spoolerr(jobX
);
81 if (write(j
, file
, strlen(file
)+1) < 0
82 || write(j
, &u
, sizeof(u
)) < 0
83 || write(j
, path
, strlen(path
)+1) < 0
85 || chmod(jobX
, 0600) < 0
92 char name
[sizeof(jobX
)];
96 /* Look for print jobs in the spool directory. Make a list of them sorted
97 * by age. Return true iff the list is nonempty.
101 struct dirent
*entry
;
102 struct job
*newjob
, **ajob
;
105 if (jobs
!= nil
) return 1;
107 if ((spool
= opendir(".")) == nil
) fatal(SPOOL
);
109 while ((entry
= readdir(spool
)) != nil
) {
110 if (strncmp(entry
->d_name
, "job", 3) != 0) continue;
112 if (stat(entry
->d_name
, &st
) < 0
113 || (st
.st_mode
& 0777) == 0000) continue;
115 if ((newjob
= malloc(sizeof(*newjob
))) == nil
) fatal("malloc()");
116 newjob
->age
= st
.st_mtime
;
117 strcpy(newjob
->name
, entry
->d_name
);
120 while (*ajob
!= nil
&& (*ajob
)->age
< newjob
->age
)
121 ajob
= &(*ajob
)->next
;
131 /* What to do with control-X:
133 * 1 give up on controlling the printer, assume user knows how printer works,
137 0, 1, 1, 1, 1, 1, 1, 0, /* \0, \a don't show. */
138 2, 2, 2, 1, 2, 2, 1, 1, /* \b, \t, \n, \f, \r */
139 1, 1, 1, 1, 1, 1, 1, 1,
140 1, 1, 1, 1, 1, 1, 1, 1
145 int count
, column
, line
, ncols
= 80, nlines
= 66;
148 /* Copy the characters in the output buffer to the printer, with retries if
155 int retry
= 0, complain
= 0;
158 while ((r
= write(lp
, bp
, count
)) < 0) {
159 if (errno
!= EAGAIN
) fatal(PRINTER
);
160 if (retry
== complain
) {
162 "lpd: %s: Printer out of paper\n",
164 complain
= retry
+ 60;
176 /* Send characters to the output buffer to be printed and do so if the buffer
177 * is full. Track the position of the write-head in `column' and `line'.
197 if (++column
> ncols
) { line
++; column
= 1; }
199 if (line
== nlines
) line
= 0;
201 if (count
== BUFSIZ
) flush();
205 /* Send the contents of an open file to the printer. Expand tabs and change
206 * linefeed to a carriage-return linefeed sequence. Print a formfeed at the
207 * end if needed to reach the top of the next page. If a control character
208 * is printed that we do not know about, then the user is assumed to know
209 * what they are doing, so output processing is disabled.
214 count
= column
= line
= 0;
216 while ((c
= getc(f
)) != EOF
) {
218 switch (control
[c
]) {
219 case 0: continue; /* Ignore this one. */
221 /* Can't handle this junk, assume smart user. */
224 if (count
== BUFSIZ
) flush();
225 } while ((c
= getc(f
)) != EOF
);
241 } while (column
& 07);
244 if (column
> 0) put(c
);
250 if (column
> 0) { put('\r'); put('\n'); }
251 if (line
> 0) put('\f');
256 void joberr(char *job
)
258 fprintf(stderr
, "lpd: something is wrong with %s\n", job
);
260 if (unlink(job
) < 0) fatal("can't remove it");
264 /* Print all the jobs in the job list. */
267 char file
[PATH_MAX
+1], *pf
=file
;
274 if ((j
= fopen(job
->name
, "r")) == nil
) {
280 if (pf
== file
+ sizeof(file
) || (c
= getc(j
)) == EOF
) {
290 if ((f
= fopen(file
, "r")) == nil
)
291 fprintf(stderr
, "lpd: can't read %s\n", file
);
296 if (file
[0] != '/' && unlink(file
) < 0) report(file
);
298 if (unlink(job
->name
) < 0) fatal(job
->name
);
303 /* Find the line printer dimensions in the termcap database under "lp". */
308 if (tgetent(printcap
, "lp") == 1) {
309 if ((n
= tgetnum("co")) > 0) ncols
= n
;
310 if ((n
= tgetnum("li")) > 0) nlines
= n
;
315 /* Become a daemon, print jobs while there are any, exit. */
319 if ((fd
= open("/dev/tty", O_RDONLY
)) != -1) {
320 /* We have a controlling tty! Disconnect. */
324 case -1: fatal("can't fork");
329 if ((fd
= open("/dev/null", O_RDONLY
)) < 0) fatal("/dev/null");
332 if ((fd
= open(LOG
, O_WRONLY
)) < 0) fatal(LOG
);
342 if ((lp
= open(PRINTER
, O_WRONLY
)) < 0) {
344 if (errno
== EBUSY
) exit(0);
348 while (job()) work();
354 int main(int argc
, char **argv
)
357 fprintf(stderr
, "Usage: %s [path | stdin < path]\n", argv
[0]);
363 if (chdir(SPOOL
) < 0) fatal(SPOOL
);
365 if (argc
== 2) spool(argv
[1]);