]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/lpr/common_source/displayq.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / usr.sbin / lpr / common_source / displayq.c
1 /*
2  * Copyright (c) 1983, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #if 0
31 #ifndef lint
32 static char sccsid[] = "@(#)displayq.c  8.4 (Berkeley) 4/28/95";
33 #endif /* not lint */
34 #endif
35
36 #include "lp.cdefs.h"           /* A cross-platform version of <sys/cdefs.h> */
37 __FBSDID("$FreeBSD$");
38
39 #include <sys/param.h>
40 #include <sys/stat.h>
41
42 #include <ctype.h>
43 #include <dirent.h>
44 #include <err.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <signal.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #define psignal foil_gcc_psignal
52 #define sys_siglist foil_gcc_siglist
53 #include <unistd.h>
54 #undef psignal
55 #undef sys_siglist
56
57 #include "lp.h"
58 #include "lp.local.h"
59 #include "pathnames.h"
60
61 /*
62  * Routines to display the state of the queue.
63  */
64 #define JOBCOL  40              /* column for job # in -l format */
65 #define OWNCOL  7               /* start of Owner column in normal */
66 #define SIZCOL  62              /* start of Size column in normal */
67
68 /*
69  * isprint() takes a parameter of 'int', but expect values in the range
70  * of unsigned char.  Define a wrapper which takes a value of type 'char',
71  * whether signed or unsigned, and ensure it ends up in the right range.
72  */
73 #define isprintch(Anychar) isprint((u_char)(Anychar))
74
75 /*
76  * Stuff for handling job specifications
77  */
78 static int      col;            /* column on screen */
79 static char     current[MAXNAMLEN+1];   /* current file being printed */
80 static char     file[MAXNAMLEN+1];      /* print file name */
81 static int      first;          /* first file in ``files'' column? */
82 static int      garbage;        /* # of garbage cf files */
83 static int      lflag;          /* long output option */
84 static int      rank;           /* order to be printed (-1=none, 0=active) */
85 static long     totsize;        /* total print job size in bytes */
86
87 static const char  *head0 = "Rank   Owner      Job  Files";
88 static const char  *head1 = "Total Size\n";
89
90 static void     alarmhandler(int _signo);
91 static void     filtered_write(char *_obuffer, int _wlen, FILE *_wstream);
92 static void     daemonwarn(const struct printer *_pp);
93
94 /*
95  * Display the current state of the queue. Format = 1 if long format.
96  */
97 void
98 displayq(struct printer *pp, int format)
99 {
100         register struct jobqueue *q;
101         register int i, nitems, fd, ret;
102         char *cp, *endp;
103         struct jobqueue **queue;
104         struct stat statb;
105         FILE *fp;
106         void (*savealrm)(int);
107
108         lflag = format;
109         totsize = 0;
110         rank = -1;
111
112         if ((cp = checkremote(pp))) {
113                 printf("Warning: %s\n", cp);
114                 free(cp);
115         }
116
117         /*
118          * Print out local queue
119          * Find all the control files in the spooling directory
120          */
121         PRIV_START
122         if (chdir(pp->spool_dir) < 0)
123                 fatal(pp, "cannot chdir to spooling directory: %s",
124                       strerror(errno));
125         PRIV_END
126         if ((nitems = getq(pp, &queue)) < 0)
127                 fatal(pp, "cannot examine spooling area\n");
128         PRIV_START
129         ret = stat(pp->lock_file, &statb);
130         PRIV_END
131         if (ret >= 0) {
132                 if (statb.st_mode & LFM_PRINT_DIS) {
133                         if (pp->remote)
134                                 printf("%s: ", local_host);
135                         printf("Warning: %s is down: ", pp->printer);
136                         PRIV_START
137                         fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
138                         PRIV_END
139                         if (fd >= 0) {
140                                 while ((i = read(fd, line, sizeof(line))) > 0)
141                                         (void) fwrite(line, 1, i, stdout);
142                                 (void) close(fd);       /* unlocks as well */
143                         } else
144                                 putchar('\n');
145                 }
146                 if (statb.st_mode & LFM_QUEUE_DIS) {
147                         if (pp->remote)
148                                 printf("%s: ", local_host);
149                         printf("Warning: %s queue is turned off\n", 
150                                pp->printer);
151                 }
152         }
153
154         if (nitems) {
155                 PRIV_START
156                 fp = fopen(pp->lock_file, "r");
157                 PRIV_END
158                 if (fp == NULL)
159                         daemonwarn(pp);
160                 else {
161                         /* get daemon pid */
162                         cp = current;
163                         endp = cp + sizeof(current) - 1;
164                         while ((i = getc(fp)) != EOF && i != '\n') {
165                                 if (cp < endp)
166                                         *cp++ = i;
167                         }
168                         *cp = '\0';
169                         i = atoi(current);
170                         if (i <= 0) {
171                                 ret = -1;
172                         } else {
173                                 PRIV_START
174                                 ret = kill(i, 0);
175                                 PRIV_END
176                         }
177                         if (ret < 0) {
178                                 daemonwarn(pp);
179                         } else {
180                                 /* read current file name */
181                                 cp = current;
182                                 endp = cp + sizeof(current) - 1;
183                                 while ((i = getc(fp)) != EOF && i != '\n') {
184                                         if (cp < endp)
185                                                 *cp++ = i;
186                                 }
187                                 *cp = '\0';
188                                 /*
189                                  * Print the status file.
190                                  */
191                                 if (pp->remote)
192                                         printf("%s: ", local_host);
193                                 PRIV_START
194                                 fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
195                                 PRIV_END
196                                 if (fd >= 0) {
197                                         while ((i = read(fd, line,
198                                                          sizeof(line))) > 0)
199                                                 fwrite(line, 1, i, stdout);
200                                         close(fd);      /* unlocks as well */
201                                 } else
202                                         putchar('\n');
203                         }
204                         (void) fclose(fp);
205                 }
206                 /*
207                  * Now, examine the control files and print out the jobs to
208                  * be done for each user.
209                  */
210                 if (!lflag)
211                         header();
212                 for (i = 0; i < nitems; i++) {
213                         q = queue[i];
214                         inform(pp, q->job_cfname);
215                         free(q);
216                 }
217                 free(queue);
218         }
219         if (!pp->remote) {
220                 if (nitems == 0)
221                         puts("no entries");
222                 return;
223         }
224
225         /*
226          * Print foreign queue
227          * Note that a file in transit may show up in either queue.
228          */
229         if (nitems)
230                 putchar('\n');
231         (void) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3',
232                         pp->remote_queue);
233         cp = line;
234         for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) {
235                 cp += strlen(cp);
236                 (void) sprintf(cp, " %d", requ[i]);
237         }
238         for (i = 0; i < users && cp - line + 1 + strlen(user[i]) < 
239                 sizeof(line) - 1; i++) {
240                 cp += strlen(cp);
241                 *cp++ = ' ';
242                 (void) strcpy(cp, user[i]);
243         }
244         strcat(line, "\n");
245         savealrm = signal(SIGALRM, alarmhandler);
246         alarm(pp->conn_timeout);
247         fd = getport(pp, pp->remote_host, 0);
248         alarm(0);
249         (void)signal(SIGALRM, savealrm);
250         if (fd < 0) {
251                 if (from_host != local_host)
252                         printf("%s: ", local_host);
253                 printf("connection to %s is down\n", pp->remote_host);
254         }
255         else {
256                 i = strlen(line);
257                 if (write(fd, line, i) != i)
258                         fatal(pp, "Lost connection");
259                 while ((i = read(fd, line, sizeof(line))) > 0)
260                         filtered_write(line, i, stdout);
261                 filtered_write(NULL, -1, stdout);
262                 (void) close(fd);
263         }
264 }
265
266 /*
267  * The lpq-info read from remote hosts may contain unprintable characters,
268  * or carriage-returns instead of line-feeds.  Clean those up before echoing
269  * the lpq-info line(s) to stdout.  The info may also be missing any kind of
270  * end-of-line character.  This also turns CRLF and LFCR into a plain LF.
271  *
272  * This routine may be called multiple times to process a single set of
273  * information, and after a set is finished this routine must be called
274  * one extra time with NULL specified as the buffer address.
275  */
276 static void
277 filtered_write(char *wbuffer, int wlen, FILE *wstream)
278 {
279         static char lastchar, savedchar;
280         char *chkptr, *dest_end, *dest_ch, *nxtptr, *w_end;
281         int destlen;
282         char destbuf[BUFSIZ];
283
284         if (wbuffer == NULL) {
285                 if (savedchar != '\0') {
286                         if (savedchar == '\r')
287                                 savedchar = '\n';
288                         fputc(savedchar, wstream);
289                         lastchar = savedchar;
290                         savedchar = '\0';
291                 }
292                 if (lastchar != '\0' && lastchar != '\n')
293                         fputc('\n', wstream);
294                 lastchar = '\0';
295                 return;
296         }
297
298         dest_ch = &destbuf[0];
299         dest_end = dest_ch + sizeof(destbuf);
300         chkptr = wbuffer;
301         w_end = wbuffer + wlen;
302         lastchar = '\0';
303         if (savedchar != '\0') {
304                 chkptr = &savedchar;
305                 nxtptr = wbuffer;
306         } else
307                 nxtptr = chkptr + 1;
308
309         while (chkptr < w_end) {
310                 if (nxtptr < w_end) {
311                         if ((*chkptr == '\r' && *nxtptr == '\n') ||
312                             (*chkptr == '\n' && *nxtptr == '\r')) {
313                                 *dest_ch++ = '\n';
314                                 /* want to skip past that second character */
315                                 nxtptr++;
316                                 goto check_next;
317                         }
318                 } else {
319                         /* This is the last byte in the buffer given on this
320                          * call, so check if it could be the first-byte of a
321                          * significant two-byte sequence.  If it is, then
322                          * don't write it out now, but save for checking in
323                          * the next call.
324                          */
325                         savedchar = '\0';
326                         if (*chkptr == '\r' || *chkptr == '\n') {
327                                 savedchar = *chkptr;
328                                 break;
329                         }
330                 }
331                 if (*chkptr == '\r')
332                         *dest_ch++ = '\n';
333 #if 0           /* XXX - don't translate unprintable characters (yet) */
334                 else if (*chkptr != '\t' && *chkptr != '\n' &&
335                     !isprintch(*chkptr))
336                         *dest_ch++ = '?';
337 #endif
338                 else
339                         *dest_ch++ = *chkptr;
340
341 check_next:
342                 chkptr = nxtptr;
343                 nxtptr = chkptr + 1;
344                 if (dest_ch >= dest_end) {
345                         destlen = dest_ch - &destbuf[0];
346                         fwrite(destbuf, 1, destlen, wstream);
347                         lastchar = destbuf[destlen - 1];
348                         dest_ch = &destbuf[0];
349                 }
350         }
351         destlen = dest_ch - &destbuf[0];
352         if (destlen > 0) {
353                 fwrite(destbuf, 1, destlen, wstream);
354                 lastchar = destbuf[destlen - 1];
355         }
356 }
357
358 /*
359  * Print a warning message if there is no daemon present.
360  */
361 static void
362 daemonwarn(const struct printer *pp)
363 {
364         if (pp->remote)
365                 printf("%s: ", local_host);
366         puts("Warning: no daemon present");
367         current[0] = '\0';
368 }
369
370 /*
371  * Print the header for the short listing format
372  */
373 void
374 header(void)
375 {
376         printf("%s", head0);
377         col = strlen(head0)+1;
378         blankfill(SIZCOL);
379         printf("%s", head1);
380 }
381
382 void
383 inform(const struct printer *pp, char *cf)
384 {
385         int copycnt, jnum;
386         char     savedname[MAXPATHLEN+1];
387         FILE    *cfp;
388
389         /*
390          * There's a chance the control file has gone away
391          * in the meantime; if this is the case just keep going
392          */
393         PRIV_START
394         if ((cfp = fopen(cf, "r")) == NULL)
395                 return;
396         PRIV_END
397
398         if (rank < 0)
399                 rank = 0;
400         if (pp->remote || garbage || strcmp(cf, current))
401                 rank++;
402
403         /*
404          * The cf-file may include commands to print more than one datafile
405          * from the user.  For each datafile, the cf-file contains at least
406          * one line which starts with some format-specifier ('a'-'z'), and
407          * a second line ('N'ame) which indicates the original name the user
408          * specified for that file.  There can be multiple format-spec lines
409          * for a single Name-line, if the user requested multiple copies of
410          * that file.  Standard lpr puts the format-spec line(s) before the
411          * Name-line, while lprNG puts the Name-line before the format-spec
412          * line(s).  This section needs to handle the lines in either order.
413          */
414         copycnt = 0;
415         file[0] = '\0';
416         savedname[0] = '\0';
417         jnum = calc_jobnum(cf, NULL);
418         while (getline(cfp)) {
419                 switch (line[0]) {
420                 case 'P': /* Was this file specified in the user's list? */
421                         if (!inlist(line+1, cf)) {
422                                 fclose(cfp);
423                                 return;
424                         }
425                         if (lflag) {
426                                 printf("\n%s: ", line+1);
427                                 col = strlen(line+1) + 2;
428                                 prank(rank);
429                                 blankfill(JOBCOL);
430                                 printf(" [job %s]\n", cf+3);
431                         } else {
432                                 col = 0;
433                                 prank(rank);
434                                 blankfill(OWNCOL);
435                                 printf("%-10s %-3d  ", line+1, jnum);
436                                 col += 16;
437                                 first = 1;
438                         }
439                         continue;
440                 default: /* some format specifer and file name? */
441                         if (line[0] < 'a' || line[0] > 'z')
442                                 break;
443                         if (copycnt == 0 || strcmp(file, line+1) != 0) {
444                                 strlcpy(file, line + 1, sizeof(file));
445                         }
446                         copycnt++;
447                         /*
448                          * deliberately 'continue' to another getline(), so
449                          * all format-spec lines for this datafile are read
450                          * in and counted before calling show()
451                          */
452                         continue;
453                 case 'N':
454                         strlcpy(savedname, line + 1, sizeof(savedname));
455                         break;
456                 }
457                 if ((file[0] != '\0') && (savedname[0] != '\0')) {
458                         show(savedname, file, copycnt);
459                         copycnt = 0;
460                         file[0] = '\0';
461                         savedname[0] = '\0';
462                 }
463         }
464         fclose(cfp);
465         /* check for a file which hasn't been shown yet */
466         if (file[0] != '\0') {
467                 if (savedname[0] == '\0') {
468                         /* a safeguard in case the N-ame line is missing */
469                         strlcpy(savedname, file, sizeof(savedname));
470                 }
471                 show(savedname, file, copycnt);
472         }
473         if (!lflag) {
474                 blankfill(SIZCOL);
475                 printf("%ld bytes\n", totsize);
476                 totsize = 0;
477         }
478 }
479
480 int
481 inlist(char *uname, char *cfile)
482 {
483         int *r, jnum;
484         char **u;
485         const char *cfhost;
486
487         if (users == 0 && requests == 0)
488                 return(1);
489         /*
490          * Check to see if it's in the user list
491          */
492         for (u = user; u < &user[users]; u++)
493                 if (!strcmp(*u, uname))
494                         return(1);
495         /*
496          * Check the request list
497          */
498         jnum = calc_jobnum(cfile, &cfhost);
499         for (r = requ; r < &requ[requests]; r++)
500                 if (*r == jnum && !strcmp(cfhost, from_host))
501                         return(1);
502         return(0);
503 }
504
505 void
506 show(const char *nfile, const char *datafile, int copies)
507 {
508         if (strcmp(nfile, " ") == 0)
509                 nfile = "(standard input)";
510         if (lflag)
511                 ldump(nfile, datafile, copies);
512         else
513                 dump(nfile, datafile, copies);
514 }
515
516 /*
517  * Fill the line with blanks to the specified column
518  */
519 void
520 blankfill(int tocol)
521 {
522         while (col++ < tocol)
523                 putchar(' ');
524 }
525
526 /*
527  * Give the abbreviated dump of the file names
528  */
529 void
530 dump(const char *nfile, const char *datafile, int copies)
531 {
532         struct stat lbuf;
533         const char etctmpl[] = ", ...";
534         char     etc[sizeof(etctmpl)];
535         char    *lastsep;
536         short    fill, nlen;
537         short    rem, remetc;
538
539         /*
540          * Print as many filenames as will fit
541          *      (leaving room for the 'total size' field)
542          */
543         fill = first ? 0 : 2;   /* fill space for ``, '' */
544         nlen = strlen(nfile);
545         rem = SIZCOL - 1 - col;
546         if (nlen + fill > rem) {
547                 if (first) {
548                         /* print the right-most part of the name */
549                         printf("...%s ", &nfile[3+nlen-rem]);
550                         col = SIZCOL;
551                 } else if (rem > 0) {
552                         /* fit as much of the etc-string as we can */
553                         remetc = rem;
554                         if (rem > strlen(etctmpl))
555                                 remetc = strlen(etctmpl);
556                         etc[0] = '\0';
557                         strncat(etc, etctmpl, remetc);
558                         printf("%s", etc);
559                         col += remetc;
560                         rem -= remetc;
561                         /* room for the last segment of this filename? */
562                         lastsep = strrchr(nfile, '/');
563                         if ((lastsep != NULL) && (rem > strlen(lastsep))) {
564                                 /* print the right-most part of this name */
565                                 printf("%s", lastsep);
566                                 col += strlen(lastsep);
567                         } else {
568                                 /* do not pack any more names in here */
569                                 blankfill(SIZCOL);
570                         }
571                 }
572         } else {
573                 if (!first)
574                         printf(", ");
575                 printf("%s", nfile);
576                 col += nlen + fill;
577         }
578         first = 0;
579
580         PRIV_START
581         if (*datafile && !stat(datafile, &lbuf))
582                 totsize += copies * lbuf.st_size;
583         PRIV_END
584 }
585
586 /*
587  * Print the long info about the file
588  */
589 void
590 ldump(const char *nfile, const char *datafile, int copies)
591 {
592         struct stat lbuf;
593
594         putchar('\t');
595         if (copies > 1)
596                 printf("%-2d copies of %-19s", copies, nfile);
597         else
598                 printf("%-32s", nfile);
599         if (*datafile && !stat(datafile, &lbuf))
600                 printf(" %qd bytes", (long long) lbuf.st_size);
601         else
602                 printf(" ??? bytes");
603         putchar('\n');
604 }
605
606 /*
607  * Print the job's rank in the queue,
608  *   update col for screen management
609  */
610 void
611 prank(int n)
612 {
613         char rline[100];
614         static const char *r[] = {
615                 "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
616         };
617
618         if (n == 0) {
619                 printf("active");
620                 col += 6;
621                 return;
622         }
623         if ((n/10)%10 == 1)
624                 (void)snprintf(rline, sizeof(rline), "%dth", n);
625         else
626                 (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
627         col += strlen(rline);
628         printf("%s", rline);
629 }
630
631 void
632 alarmhandler(int signo __unused)
633 {
634         /* the signal is ignored */
635         /* (the '__unused' is just to avoid a compile-time warning) */
636 }