* reordered a little bit
[mascara-docs.git] / i86 / elks / elkscmd / minix2 / lpd.c
blob6a420bcb9eb7a3b2407742a983d55d48a548ee77
1 /* lpd 1.5 - Printer daemon Author: Kees J. Bot
2 * 3 Dec 1989
3 */
4 #define nil 0
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <limits.h>
8 #include <string.h>
9 #include <errno.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <dirent.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <termcap.h>
17 char PRINTER[] = "/dev/lp";
18 char SPOOL[] = "/usr/spool/lpd";
19 char LOG[] = "/dev/log";
21 void report(char *mess) {
22 fprintf(stderr, "lpd: %s: %s\n", mess, strerror(errno));
25 void fatal(char *mess) {
26 report(mess);
27 exit(1);
30 char jobX[] = "jobXXXXXX";
31 char tmpX[] = "tmpXXXXXX";
33 void spoolerr(char *file) {
34 int e = errno;
36 unlink(jobX);
37 unlink(tmpX);
38 fatal(file);
41 void spool(char *path) {
43 /* Place a file into the spool directory, either by copying it,
44 * or by leaving a reference.
47 char *file;
48 int j, u;
50 mktemp(jobX);
51 file= mktemp(tmpX);
53 if (path[0] == '/') {
54 int f;
56 if ((f= open(path, O_RDONLY)) >= 0) {
57 close(f);
58 file= path;
61 if (file != path) {
62 int c;
63 FILE *t;
65 if ((t= fopen(tmpX, "w")) == nil)
66 spoolerr(tmpX);
68 while ( (c = getchar()) != EOF && putc(c,t) != EOF)
69 /* Do nothing */;
71 if (ferror(stdin))
72 spoolerr(path);
74 if (ferror(t) || fclose(t) == EOF)
75 spoolerr(tmpX);
77 fclose(stdin);
80 if ((j= open(jobX, O_WRONLY|O_CREAT|O_EXCL, 0000)) < 0)
81 spoolerr(jobX);
83 u= getuid();
84 if (write(j, file, strlen(file)+1) < 0 || write(j, &u, sizeof(u)) < 0
85 || write(j, path, strlen(path)+1) < 0
86 || close(j) < 0
87 || chmod(jobX, 0600) < 0)
88 spoolerr(jobX);
91 struct job {
92 struct job *next;
93 time_t age;
94 char name[sizeof(jobX)];
95 } *jobs = nil;
97 int job(void) {
99 /* Look for print jobs in the spool directory. Make a list of them sorted
100 * by age. Return true iff the list is nonempty.
103 DIR *spool;
104 struct dirent *entry;
105 struct job *newjob, **ajob;
106 struct stat st;
108 if (jobs != nil)
109 return 1;
111 if ((spool= opendir(".")) == nil)
112 fatal(SPOOL);
114 while ((entry= readdir(spool)) != nil) {
115 if (strncmp(entry->d_name, "job", 3) != 0)
116 continue;
118 if (stat(entry->d_name, &st) < 0 || (st.st_mode & 0777) == 0000)
119 continue;
121 if ((newjob= malloc(sizeof(*newjob))) == nil)
122 fatal("malloc()");
124 newjob->age = st.st_mtime;
125 strcpy(newjob->name, entry->d_name);
127 ajob= &jobs;
128 while (*ajob != nil && (*ajob)->age < newjob->age)
129 ajob= &(*ajob)->next;
131 newjob->next= *ajob;
132 *ajob= newjob;
134 closedir(spool);
136 return jobs != nil;
139 /* What to do with control-X:
140 * 0 ignore,
141 * 1 give up on controlling the printer,
142 * assume user knows how printer works,
143 * 2 print.
145 char control[] = {
146 0, 1, 1, 1, 1, 1, 1, 0, /* \0, ^G don't show. */
147 1, 2, 2, 1, 2, 2, 1, 1, /* \t, \n, \f, \r */
148 1, 1, 1, 1, 1, 1, 1, 1,
149 1, 1, 1, 1, 1, 1, 1, 1
152 int lp;
153 char buf[BUFSIZ];
154 int count, column, line, ncols = 80, nlines = 66;
156 int flush(void) {
158 /* Copy the characters in the output buffer to the printer, with retries if
159 * out of paper.
162 char *bp= buf;
164 while (count > 0) {
165 int retry = 0, complain = 0;
166 int r;
168 while ((r= write(lp, bp, count)) < 0) {
169 if (errno != EAGAIN)
170 fatal(PRINTER);
171 if (retry == complain) {
172 fprintf(stderr, "lpd: %s: Printer out of paper\n", PRINTER);
173 complain= retry + 60;
175 sleep(1);
176 retry++;
178 bp+= r;
179 count-= r;
181 count = 0;
184 int put(int c) {
186 /* Send characters to the output buffer to be printed and do so if the buffer
187 * is full. Track the position of the write-head in `column' and `line'.
190 buf[count++] = c;
192 if (c == '\f') {
193 column = 0;
194 line = 0;
195 } else if (c == '\r') {
196 column = 0;
197 } else if (c == '\n') {
198 line++;
199 } else if (++column > ncols) {
200 line++;
201 column= 1;
203 if (line == nlines)
204 line= 0;
206 if (count == BUFSIZ)
207 flush();
210 void print(FILE *f) {
212 /* Send the contents of an open file to the printer. Expand tabs and change
213 * linefeed to a carriage-return linefeed sequence. Print a formfeed at the
214 * end if needed to reach the top of the next page. If a control character
215 * is printed that we do not know about, then the user is assumed to know
216 * what they are doing, so output processing is disabled.
219 int c;
221 count = column = line = 0;
223 while ((c= getc(f)) != EOF) {
224 if (c < ' ') {
225 switch (c) {
226 case '\t': /* Tab spaces to next 8th column */
227 do {
228 put(' ');
229 } while (column & 7);
230 break;
231 case '\n': /* Newline => CR/LF */
232 put('\r');
233 put(c);
234 break;
235 default: /* Check anything else */
236 switch(control[c]) {
237 case 1: /* Assume smart user */
238 do {
239 buf[count++] = c;
240 if (count == BUFSIZ)
241 flush();
242 } while ((c = getc(f)) != EOF);
243 flush();
244 return;
245 case 2: /* Print this one */
246 put(c);
247 case 0: /* Ignore this one */
248 break;
250 break;
252 } else
253 put(c);
255 if (column > 0) {
256 put('\r');
257 put('\n');
259 if (line > 0)
260 put('\f');
261 flush();
262 return;
265 void joberr(char *job) {
266 fprintf(stderr, "lpd: something is wrong with %s\n", job);
267 if (unlink(job) < 0)
268 fatal("can't remove it");
271 void work(void) {
273 /* Print all the jobs in the job list. */
275 char file[257], *pf=file;
276 struct job *job;
277 FILE *j, *f;
278 int c;
280 job = jobs;
281 jobs = jobs->next;
283 if ((j= fopen(job->name, "r")) == nil) {
284 joberr(job->name);
285 return;
288 do {
289 if (pf == file + sizeof(file) || (c= getc(j)) == EOF) {
290 fclose(j);
291 joberr(job->name);
292 return;
294 *pf++ = c;
295 } while (c != 0);
297 fclose(j);
299 if ((f= fopen(file, "r")) == nil)
300 fprintf(stderr, "lpd: can't read %s\n", file);
301 else {
302 print(f);
303 fclose(f);
305 if (file[0] != '/' && unlink(file) < 0)
306 report(file);
308 if (unlink(job->name) < 0)
309 fatal(job->name);
310 free(job);
313 void getcap(void) {
315 /* Find the line printer dimensions in the termcap database under "lp". */
317 char printcap[1024];
318 int n;
320 if (tgetent(printcap, "lp") == 1) {
321 if ((n= tgetnum("co")) > 0) ncols= n;
322 if ((n= tgetnum("li")) > 0) nlines= n;
326 void haunt(void) {
328 /* Become a daemon, print jobs while there are any, exit. */
330 int fd;
332 if ((fd= open("/dev/tty", O_RDONLY)) != -1) {
333 /* We have a controlling tty! Disconnect. */
334 close(fd);
336 switch(fork()) {
337 case -1:
338 fatal("can't fork");
339 case 0:
340 break;
341 default:
342 exit(0);
345 if ((fd = open("/dev/null", O_RDONLY)) < 0)
346 fatal("/dev/null");
347 dup2(fd, 0);
348 close(fd);
349 if ((fd= open(LOG, O_WRONLY)) < 0)
350 fatal(LOG);
351 dup2(fd, 1);
352 dup2(fd, 2);
353 close(fd);
354 setsid();
357 getcap();
359 do {
360 if ((lp = open(PRINTER, O_WRONLY)) < 0) {
361 /* Another lpd? */
362 if (errno == EBUSY)
363 exit(0);
364 fatal(PRINTER);
367 while (job())
368 work();
370 close(lp);
371 } while (job());
374 int main(int argc, char **argv) {
375 if (argc > 2) {
376 fprintf(stderr, "Usage: %s [path | stdin < path]\n", argv[0]);
377 exit(1);
380 umask(0077);
382 if (chdir(SPOOL) < 0)
383 fatal(SPOOL);
385 if (argc == 2)
386 spool(argv[1]);
388 haunt();