1: /* %M% %I% %E% */
2: /*
3: * Spool Queue examination program
4: *
5: * lpq [+[n]] [-Pprinter] [users...]
6: *
7: * + =>'s continually scan q until empty
8: * -P used to identify printer as per lpr/lpd
9: */
10:
11: #include <stdio.h>
12: #include <sys/types.h>
13: #include <sys/dir.h>
14: #include <pwd.h>
15: #include <sgtty.h>
16: #include <sys/stat.h>
17: #include <ctype.h>
18: #include "lp.local.h"
19:
20: #define MAXUSERS 50
21: #define MAXJOBS 50
22: #define READ 0
23: #define DEFTIME 10 /* default sleep interval */
24: #define JOBCOL 40 /* column for job # in -l format */
25: #define OWNCOL 7 /* start of Owner column in normal */
26:
27: struct passwd user[MAXUSERS]; /* when users are specified */
28: struct direct *pdir; /* for sorting stuff */
29:
30: int jobs[MAXJOBS]; /* jobs specified on command line */
31: int njobs = 0; /* # of entries in jobs */
32: int users; /* # of users in user or < 0 */
33: int current_pid; /* current daemon file pid */
34: int garbage = 0; /* # of garbage df files */
35: int rank = 0; /* order to be printed */
36: int slptime = DEFTIME; /* pause between screen refereshes */
37: int repeat = 0; /* + flag indicator */
38: int col; /* column on screen */
39: int lflag = 0; /* long output option */
40: int first; /* first file in ``files'' column ? */
41: int SIZCOL = 62; /* start of Size column in normal */
42:
43: char line[132]; /* input line from daemon file */
44: char file[132]; /* print file name guess */
45: char ufile[132]; /* unlink file */
46: char *head0 = "Rank Owner Job # Files";
47: char *head1 = "Total Size\n";
48:
49: long totsize = 0; /* total print job size in bytes */
50:
51: /*
52: * Printcap (a la termcap) stuff for mutiple printers
53: */
54: char *SD; /* spooling directory */
55: char *LO; /* name of lock file */
56: char *LP; /* line printer name */
57: char *pgetstr();
58:
59: struct passwd *getpwnam(), *getpwuid();
60: char *index();
61: char *getenv();
62:
63: main(argc, argv)
64: char *argv[];
65: {
66: register struct passwd *p;
67: register int n;
68:
69: argv++;
70: while (argc > 1) {
71: if (argv[0][0] == '+') {
72: if (argv[0][1] != '\0')
73: if ((slptime = atoi(&argv[0][1])) < 0)
74: slptime = DEFTIME;
75: repeat++;
76: } else if (argv[0][0] == '-')
77: switch(argv[0][1]) {
78:
79: case 'P': /* printer name */
80: if (!chkprinter(&argv[0][2]))
81: fatal("%s: unknown printer", &argv[0][2]);
82: break;
83:
84: case 'l': /* long output */
85: lflag++;
86: break;
87:
88: default:
89: usage();
90: } else {
91: if (isdigit(argv[0][0])) {
92: if (njobs >= MAXJOBS)
93: fatal("too many jobs requested");
94: jobs[njobs++] = atoi(argv[0]);
95: } else {
96: if (users >= MAXUSERS)
97: fatal("too many users");
98: p = getpwnam(*argv);
99: if (p)
100: user[users++] = *p;
101: else
102: printf("unknown user %s\n", *argv);
103: }
104: }
105: argc--;
106: argv++;
107: }
108: if (!users && !njobs)
109: users = -1;
110: if (SD == NULL) {
111: char *pr;
112:
113: if ((pr = getenv("PRINTER")) == NULL)
114: pr = DEFLP;
115: if (!chkprinter(pr))
116: fatal("%s: unknown printer", pr);
117: }
118: if (chdir(SD) < 0)
119: fatal("can't chdir to spooling area");
120:
121: if (repeat)
122: do {
123: if ((n = display()) > 0) {
124: sleep(slptime);
125: rank = 0;
126: }
127: } while (n > 0);
128: else
129: display();
130: }
131:
132: /*
133: * Display the current state of the q
134: */
135: display()
136: {
137: register int i, nitems;
138: int spfd;
139: struct stat statb;
140: int dcomp();
141:
142: /*
143: * Find all the spool files in the spooling directory
144: */
145: if ((spfd = open(".", READ)) < 0)
146: fatal("can't examine spooling area");
147: lseek(spfd, (long)(2*sizeof(struct direct)), 0);
148: pdir = (struct direct *)malloc(sizeof(struct direct));
149: nitems = 0;
150: while (1) {
151: struct direct *proto;
152:
153: proto = &pdir[nitems];
154: if (read(spfd, (char *)proto, sizeof(*proto)) != sizeof(*proto))
155: break;
156: if (proto->d_ino == 0 || proto->d_name[0] != 'd' ||
157: proto->d_name[1] != 'f')
158: continue; /* just daemon files */
159: nitems++;
160: proto = (struct direct *)realloc((char *)pdir,
161: (nitems+1)*sizeof(struct direct));
162: if (proto == NULL) {
163: standout(stdout, "out of memory, only showing %d jobs\n", nitems);
164: break;
165: }
166: pdir = proto;
167: }
168: if (nitems == 0) {
169: printf("no entries\n");
170: return(0);
171: }
172: close(spfd);
173: if ((spfd = open(LO, READ)) < 0)
174: garbage = nitems;
175: else {
176: lseek(spfd, (long)sizeof(int), 0); /* skip daemon id */
177: if (read(spfd, (char *)¤t_pid, sizeof(int)) != sizeof(int))
178: current_pid = -1; /* should be invalid */
179: close(spfd);
180: }
181: qsort(pdir, nitems, sizeof(struct direct), dcomp);
182: /*
183: * Now, examine the daemon files and print out the jobs to
184: * be done for each user
185: */
186: if (!lflag && garbage < nitems)
187: header();
188: for (i = garbage; i < nitems; i++)
189: inform(pdir[i].d_name, 0);
190:
191: /*
192: * What's left is garbage, inform the user
193: */
194: if (garbage > 0) {
195: register short down = stat(LP, &statb) >= 0 &&
196: (statb.st_mode&0777) == 0;
197: standout(stdout, down ? "Warning: printer down" :
198: "Warning: no daemon present");
199: putchar('\n');
200: if (!lflag)
201: header();
202: for (i = 0; i < garbage; i++)
203: inform(pdir[i].d_name, 1);
204: }
205: return(nitems-garbage);
206: }
207:
208: /*
209: * Print the header for the short listing format
210: */
211: ()
212: {
213: printf(head0);
214: col = strlen(head0)+1;
215: blankfill(SIZCOL);
216: printf(head1);
217: }
218:
219: /*
220: * If we have the capability, print this in standout mode
221: */
222: standout(f, s, a)
223: FILE *f;
224: register char *s;
225: {
226: }
227:
228: dcomp(d1, d2)
229: register struct direct *d1, *d2;
230: {
231: return(strncmp(d1->d_name, d2->d_name, DIRSIZ));
232: }
233:
234: inform(df, garb)
235: char *df;
236: {
237: register int j, k;
238: register struct passwd *p;
239: FILE *fd;
240: int spfd, dfpid = atoi(df+3);
241: char *owner;
242: struct stat buf;
243:
244: /*
245: * There's a chance the daemon file has gone away
246: * in the meantime; if this is the case just keep going
247: */
248: if ((spfd = open(df, READ)) < 0)
249: return;
250:
251: fstat(spfd, &buf);
252: /*
253: * Was this file specified in the user's list
254: */
255: for (j = 0; j < users; j++)
256: if (user[j].pw_uid == buf.st_uid)
257: break;
258: if (j >= users) { /* scan jobs list */
259: for (k = 0; k < njobs; k++)
260: if (dfpid == jobs[k])
261: break;
262: } else
263: k = njobs;
264: if (users < 0 || j < users || k < njobs) { /* found one */
265: if (lflag)
266: putchar('\n');
267: col = 0;
268: if (users < 0 || k < njobs)
269: owner = (p = getpwuid(buf.st_uid)) == NULL ? "???" :
270: p->pw_name;
271: else if ((owner = user[j].pw_name) == NULL)
272: owner = "???";
273: if (lflag)
274: col += strlen(owner);
275: if (!garb && dfpid == current_pid)
276: rank = 0;
277: else
278: rank++;
279: if (lflag) {
280: printf("%s: ", owner), col += 2;
281: prank(rank);
282: blankfill(JOBCOL);
283: printf(" [job #%d]\n", dfpid);
284: } else {
285: prank(rank);
286: blankfill(OWNCOL);
287: printf("%-10s %-5d ", owner, dfpid), col += 18;
288: first = 1;
289: }
290:
291: /*
292: * Now list the files associated with the
293: * print job
294: */
295: fd = fdopen(spfd, "r");
296: j = 0; /* # of copies of a file */
297: *file = *ufile = '\0';
298:
299: while (fgets(line, sizeof(line), fd) != NULL) {
300:
301: switch(line[0]) {
302:
303: default:
304: continue;
305: case 'N':
306: if (*file) {
307: /*
308: * Could miss a file which has been
309: * linked...
310: */
311: if (strcmp(file, line+1)) {
312: show(file, file, j);
313: j = 0;
314: if (strcmp(line+1, " \n"))
315: strcpy(file, line+1);
316: continue;
317: }
318: j++;
319: } else if (strcmp(line+1, " \n"))
320: strcpy(file, line+1);
321: k++;
322: continue;
323: case 'U':
324: strcpy(ufile, line+1);
325: break;
326:
327: }
328: show(file, ufile, j);
329: j = 0;
330: }
331: fclose(fd);
332: if (*file) /* there's a file left over */
333: show(file, ufile, j);
334: if (!lflag) {
335: blankfill(SIZCOL);
336: printf("%D bytes\n", totsize);
337: totsize = 0;
338: }
339: } else
340: rank++;
341: close(spfd);
342: }
343:
344: show(file, ufile, copies)
345: register char *ufile, *file;
346: {
347: register char *t;
348:
349: if ((t = index(file, '\n')) == NULL)
350: strcpy(file, "(standard input)");
351: else
352: *t = '\0';
353: if ((t = index(ufile, '\n')) != NULL)
354: *t = '\0';
355: if (lflag)
356: ldump(file, *ufile ? ufile : file, copies);
357: else
358: dump(file, *ufile ? ufile : file, copies);
359: *ufile = *file = '\0';
360: }
361:
362: /*
363: * Fill the line with blanks to the specified column
364: */
365: blankfill(n)
366: register int n;
367: {
368: while (col++ < n)
369: putchar(' ');
370: }
371:
372: /*
373: * Give the abbreviated dump of the file names
374: */
375: dump(file, ufile, copies)
376: char *file, *ufile;
377: {
378: register short n, fill;
379: struct stat lbuf;
380:
381: /*
382: * Print as many files as will fit
383: * (leaving room for the total size)
384: */
385: fill = first ? 0 : 2; /* fill space for ``, '' */
386: if (((n = strlen(file)) + col + fill) >= SIZCOL-4) {
387: if (col < SIZCOL) {
388: printf(" ..."), col += 4;
389: blankfill(SIZCOL);
390: }
391: } else {
392: if (first)
393: first = 0;
394: else
395: printf(", ");
396: printf("%s", file);
397: col += n+fill;
398: }
399: if (*ufile && !stat(ufile, &lbuf))
400: totsize += copies ? (copies+1)*lbuf.st_size : lbuf.st_size;
401: }
402:
403: /*
404: * Print the long info about the file
405: */
406: ldump(file, ufile, copies)
407: char *file, *ufile;
408: {
409: struct stat lbuf;
410:
411: putchar('\t');
412: if (copies)
413: printf("%-2d copies of %-19s", copies+1, file);
414: else
415: printf("%-32s", file);
416: if (*ufile) {
417: if (!stat(ufile, &lbuf))
418: printf(" %D bytes", lbuf.st_size);
419: else
420: printf(" ??? bytes");
421: } else
422: printf(" ??? bytes");
423: putchar('\n');
424: }
425:
426: /*
427: * Print the job's rank in the queue,
428: * update col for screen management
429: */
430: prank(n)
431: {
432: char line[100];
433: static char *r[] = {
434: "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
435: };
436:
437: if (n == 0) {
438: printf("active");
439: col += 6;
440: return;
441: }
442: if ((n/10) == 1)
443: sprintf(line, "%dth", n);
444: else
445: sprintf(line, "%d%s", n, r[n%10]);
446: col += strlen(line);
447: printf("%s", line);
448: }
449:
450: usage()
451: {
452: printf("usage: lpq [-l] [+[n]] [-Pprinter] [users...]\n");
453: exit(1);
454: }
455:
456: fatal(s, a)
457: char *s;
458: {
459: standout(stderr, "lpq: ");
460: standout(stderr, s, a);
461: putc('\n', stderr);
462: exit(2);
463: }
464:
465: /*
466: * Interrogate the printer data base
467: */
468: chkprinter(s)
469: char *s;
470: {
471: static char buf[BUFSIZ/2];
472: char b[BUFSIZ];
473: int stat;
474: char *bp = buf;
475:
476: if ((stat = pgetent(b, s)) < 0)
477: fatal("can't open description file");
478: else if (stat == 0)
479: return(0);
480: if ((LP = pgetstr("lp", &bp)) == NULL)
481: LP = DEFDEVLP;
482: if ((SD = pgetstr("sd", &bp)) == NULL)
483: SD = DEFSPOOL;
484: if ((LO = pgetstr("lo", &bp)) == NULL)
485: LO = DEFLOCK;
486: return(1);
487: }