1*7027866aSroy /*	$NetBSD: displayq.c,v 1.34 2009/07/13 19:05:41 roy Exp $	*/
221908ddbSjtc 
361f28255Scgd /*
42847add2Scgd  * Copyright (c) 1983, 1993
52847add2Scgd  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * Redistribution and use in source and binary forms, with or without
861f28255Scgd  * modification, are permitted provided that the following conditions
961f28255Scgd  * are met:
1061f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1161f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1261f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1361f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1461f28255Scgd  *    documentation and/or other materials provided with the distribution.
15326b2259Sagc  * 3. Neither the name of the University nor the names of its contributors
1661f28255Scgd  *    may be used to endorse or promote products derived from this software
1761f28255Scgd  *    without specific prior written permission.
1861f28255Scgd  *
1961f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2061f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2161f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2261f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2361f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2461f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2561f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2661f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2761f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2861f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2961f28255Scgd  * SUCH DAMAGE.
3061f28255Scgd  */
3161f28255Scgd 
32fe7ed7ceSmrg #include <sys/cdefs.h>
3361f28255Scgd #ifndef lint
34ad1d6861Smikel #if 0
35e6a91a09Smrg static char sccsid[] = "@(#)displayq.c	8.4 (Berkeley) 4/28/95";
36ad1d6861Smikel #else
37*7027866aSroy __RCSID("$NetBSD: displayq.c,v 1.34 2009/07/13 19:05:41 roy Exp $");
38ad1d6861Smikel #endif
3961f28255Scgd #endif /* not lint */
4061f28255Scgd 
412847add2Scgd #include <sys/param.h>
422847add2Scgd #include <sys/stat.h>
43e6a91a09Smrg #include <sys/file.h>
442847add2Scgd 
452847add2Scgd #include <signal.h>
462847add2Scgd #include <fcntl.h>
472847add2Scgd #include <dirent.h>
482847add2Scgd #include <unistd.h>
492847add2Scgd #include <stdio.h>
502847add2Scgd #include <stdlib.h>
512847add2Scgd #include <string.h>
522847add2Scgd #include <ctype.h>
532847add2Scgd #include "lp.h"
542847add2Scgd #include "lp.local.h"
552847add2Scgd #include "pathnames.h"
562847add2Scgd 
5761f28255Scgd /*
5861f28255Scgd  * Routines to display the state of the queue.
5961f28255Scgd  */
6061f28255Scgd #define JOBCOL	40		/* column for job # in -l format */
6161f28255Scgd #define OWNCOL	7		/* start of Owner column in normal */
6261f28255Scgd #define SIZCOL	62		/* start of Size column in normal */
6361f28255Scgd 
6461f28255Scgd /*
6561f28255Scgd  * Stuff for handling job specifications
6661f28255Scgd  */
6761f28255Scgd extern int	requ[];		/* job number of spool entries */
6861f28255Scgd extern int	requests;	/* # of spool requests */
692847add2Scgd extern char    *user[];	        /* users to process */
702847add2Scgd extern int	users;		/* # of users in user array */
7161f28255Scgd 
728e41ca80Shpeyerl extern uid_t	uid, euid;
738e41ca80Shpeyerl 
742847add2Scgd static int	col;		/* column on screen */
75c9e786ceSitojun static char	current[MAXPATHLEN]; /* current file being printed */
7604723c3fSchristos static char	fname[MAXPATHLEN]; /* print file name */
772847add2Scgd static int	first;		/* first file in ``files'' column? */
782847add2Scgd static int	garbage;	/* # of garbage cf files */
792847add2Scgd static int	lflag;		/* long output option */
802847add2Scgd static int	rank;		/* order to be printed (-1=none, 0=active) */
812847add2Scgd static long	totsize;	/* total print job size in bytes */
8261f28255Scgd 
83d8302e2dSis static const char head0[] = "Rank   Owner      Job  Files";
84d8302e2dSis static const char head1[] = "Total Size\n";
8561f28255Scgd 
86895dc72aSwiz static	void	alarmer(int);
875b6d0e7eSmrg 
885b6d0e7eSmrg int wait_time = 300;	/* time out after 5 minutes by default */
895b6d0e7eSmrg 
9061f28255Scgd /*
9161f28255Scgd  * Display the current state of the queue. Format = 1 if long format.
9261f28255Scgd  */
932847add2Scgd void
displayq(int format)94895dc72aSwiz displayq(int format)
9561f28255Scgd {
96fe7ed7ceSmrg 	struct queue *q;
97fe7ed7ceSmrg 	int i, nitems, fd, ret;
98c6813048Schristos 	char *cp, *ecp;
9961f28255Scgd 	struct queue **queue;
10061f28255Scgd 	struct stat statb;
10161f28255Scgd 	FILE *fp;
10261f28255Scgd 
10361f28255Scgd 	lflag = format;
10461f28255Scgd 	totsize = 0;
10561f28255Scgd 	rank = -1;
10604723c3fSchristos 	getprintcap(printer);
10761f28255Scgd 
10861f28255Scgd 	/*
10961f28255Scgd 	 * Print out local queue
11061f28255Scgd 	 * Find all the control files in the spooling directory
11161f28255Scgd 	 */
1128e41ca80Shpeyerl 	seteuid(euid);
11361f28255Scgd 	if (chdir(SD) < 0)
11461f28255Scgd 		fatal("cannot chdir to spooling directory");
1158e41ca80Shpeyerl 	seteuid(uid);
11661f28255Scgd 	if ((nitems = getq(&queue)) < 0)
11761f28255Scgd 		fatal("cannot examine spooling area\n");
1188e41ca80Shpeyerl 	seteuid(euid);
1198e41ca80Shpeyerl 	ret = stat(LO, &statb);
1208e41ca80Shpeyerl 	seteuid(uid);
1218e41ca80Shpeyerl 	if (ret >= 0) {
1225da00734Sitojun 		if (statb.st_mode & S_IXUSR) {
123e6a91a09Smrg 			if (remote)
12461f28255Scgd 				printf("%s: ", host);
12561f28255Scgd 			printf("Warning: %s is down: ", printer);
1268e41ca80Shpeyerl 			seteuid(euid);
12761f28255Scgd 			fd = open(ST, O_RDONLY);
1288e41ca80Shpeyerl 			seteuid(uid);
12961f28255Scgd 			if (fd >= 0) {
13061f28255Scgd 				(void)flock(fd, LOCK_SH);
13161f28255Scgd 				while ((i = read(fd, line, sizeof(line))) > 0)
13223b9fac0Smrg 					(void)fwrite(line, 1, (size_t)i, stdout);
13361f28255Scgd 				(void)close(fd);	/* unlocks as well */
13461f28255Scgd 			} else
13561f28255Scgd 				putchar('\n');
13661f28255Scgd 		}
1375da00734Sitojun 		if (statb.st_mode & S_IXGRP) {
138e6a91a09Smrg 			if (remote)
13961f28255Scgd 				printf("%s: ", host);
14061f28255Scgd 			printf("Warning: %s queue is turned off\n", printer);
14161f28255Scgd 		}
14261f28255Scgd 	}
14361f28255Scgd 
14461f28255Scgd 	if (nitems) {
1458e41ca80Shpeyerl 		seteuid(euid);
14661f28255Scgd 		fp = fopen(LO, "r");
1478e41ca80Shpeyerl 		seteuid(uid);
14861f28255Scgd 		if (fp == NULL)
149fe7ed7ceSmrg 			nodaemon();
15061f28255Scgd 		else {
15161f28255Scgd 			/* get daemon pid */
15261f28255Scgd 			cp = current;
153c9e786ceSitojun 			ecp = cp + sizeof(current) - 1;
154c9e786ceSitojun 			while ((i = getc(fp)) != EOF && i != '\n') {
155c9e786ceSitojun 				if (cp < ecp)
156fe7ed7ceSmrg 					*cp++ = i;
157c9e786ceSitojun 			}
15861f28255Scgd 			*cp = '\0';
15961f28255Scgd 			i = atoi(current);
1608e41ca80Shpeyerl 			if (i <= 0) {
1610e9c04faSpk 				ret = -1;
1620e9c04faSpk 			} else {
1638e41ca80Shpeyerl 				seteuid(euid);
1648e41ca80Shpeyerl 				ret = kill(i, 0);
1658e41ca80Shpeyerl 				seteuid(uid);
1668e41ca80Shpeyerl 			}
1678e41ca80Shpeyerl 			if (ret < 0) {
168fe7ed7ceSmrg 				nodaemon();
1690e9c04faSpk 			} else {
17061f28255Scgd 				/* read current file name */
17161f28255Scgd 				cp = current;
172c9e786ceSitojun 		    		ecp = cp + sizeof(current) - 1;
173c9e786ceSitojun 				while ((i = getc(fp)) != EOF && i != '\n') {
174c9e786ceSitojun 					if (cp < ecp)
175fe7ed7ceSmrg 						*cp++ = i;
176c9e786ceSitojun 				}
17761f28255Scgd 				*cp = '\0';
17861f28255Scgd 				/*
17961f28255Scgd 				 * Print the status file.
18061f28255Scgd 				 */
181e6a91a09Smrg 				if (remote)
18261f28255Scgd 					printf("%s: ", host);
1838e41ca80Shpeyerl 				seteuid(euid);
18461f28255Scgd 				fd = open(ST, O_RDONLY);
1858e41ca80Shpeyerl 				seteuid(uid);
18661f28255Scgd 				if (fd >= 0) {
18761f28255Scgd 					(void)flock(fd, LOCK_SH);
18861f28255Scgd 					while ((i = read(fd, line, sizeof(line))) > 0)
18923b9fac0Smrg 						(void)fwrite(line, 1, (size_t)i, stdout);
19061f28255Scgd 					(void)close(fd);	/* unlocks as well */
19161f28255Scgd 				} else
19261f28255Scgd 					putchar('\n');
19361f28255Scgd 			}
19461f28255Scgd 			(void)fclose(fp);
19561f28255Scgd 		}
19661f28255Scgd 		/*
19761f28255Scgd 		 * Now, examine the control files and print out the jobs to
19861f28255Scgd 		 * be done for each user.
19961f28255Scgd 		 */
20061f28255Scgd 		if (!lflag)
20161f28255Scgd 			header();
20261f28255Scgd 		for (i = 0; i < nitems; i++) {
20361f28255Scgd 			q = queue[i];
20461f28255Scgd 			inform(q->q_name);
20561f28255Scgd 		}
20661f28255Scgd 	}
2074a6e5a62Schristos 	freeq(queue, nitems);
208e6a91a09Smrg 	if (!remote) {
20961f28255Scgd 		if (nitems == 0)
21061f28255Scgd 			puts("no entries");
21161f28255Scgd 		return;
21261f28255Scgd 	}
21361f28255Scgd 
21461f28255Scgd 	/*
21561f28255Scgd 	 * Print foreign queue
21661f28255Scgd 	 * Note that a file in transit may show up in either queue.
21761f28255Scgd 	 */
21861f28255Scgd 	if (nitems)
21961f28255Scgd 		putchar('\n');
220077acf50Smrg 	(void)snprintf(line, sizeof(line), "%c%s", format + '\3', RP);
22161f28255Scgd 	cp = line;
22285da8822Sitojun 	ecp = line + sizeof(line);
223f5a433a7Slukem 	for (i = 0;
224f5a433a7Slukem 	    i < requests && (size_t)(cp - line + 11) < sizeof(line) - 2;
225f5a433a7Slukem 	    i++) {
22661f28255Scgd 		cp += strlen(cp);
22785da8822Sitojun 		(void)snprintf(cp, ecp - cp, " %d", requ[i]);
22861f28255Scgd 	}
229f5a433a7Slukem 	for (i = 0;
230f5a433a7Slukem 	    i < users && cp - line + 1 + strlen(user[i]) < sizeof(line) - 2;
231f5a433a7Slukem 	    i++) {
23261f28255Scgd 		cp += strlen(cp);
233f5a433a7Slukem 		if ((size_t)(cp - line) > sizeof(line) - 2)
234077acf50Smrg 			break;
23561f28255Scgd 		*cp++ = ' ';
23685da8822Sitojun 		/* truncation may happen */
23785da8822Sitojun 		(void)strlcpy(cp, user[i], ecp - cp);
23861f28255Scgd 	}
23985da8822Sitojun 	(void)strlcat(line, "\n", sizeof(line));
240c6813048Schristos 	fd = getport(RM);
24161f28255Scgd 	if (fd < 0) {
24261f28255Scgd 		if (from != host)
24361f28255Scgd 			printf("%s: ", host);
244077acf50Smrg 		(void)printf("connection to %s is down\n", RM);
24561f28255Scgd 	}
24661f28255Scgd 	else {
2475b6d0e7eSmrg 		struct sigaction osa, nsa;
2485b6d0e7eSmrg 
24961f28255Scgd 		i = strlen(line);
25023b9fac0Smrg 		if (write(fd, line, (size_t)i) != i)
25161f28255Scgd 			fatal("Lost connection");
2525b6d0e7eSmrg 		nsa.sa_handler = alarmer;
2535b6d0e7eSmrg 		sigemptyset(&nsa.sa_mask);
2545b6d0e7eSmrg 		sigaddset(&nsa.sa_mask, SIGALRM);
2555b6d0e7eSmrg 		nsa.sa_flags = 0;
2565b6d0e7eSmrg 		(void)sigaction(SIGALRM, &nsa, &osa);
2575b6d0e7eSmrg 		alarm(wait_time);
2585b6d0e7eSmrg 		while ((i = read(fd, line, sizeof(line))) > 0) {
25923b9fac0Smrg 			(void)fwrite(line, 1, (size_t)i, stdout);
2605b6d0e7eSmrg 			alarm(wait_time);
2615b6d0e7eSmrg 		}
2625b6d0e7eSmrg 		alarm(0);
2635b6d0e7eSmrg 		(void)sigaction(SIGALRM, &osa, NULL);
26461f28255Scgd 		(void)close(fd);
26561f28255Scgd 	}
26661f28255Scgd }
26761f28255Scgd 
2685b6d0e7eSmrg static void
alarmer(int s)269895dc72aSwiz alarmer(int s)
2705b6d0e7eSmrg {
2715b6d0e7eSmrg 	/* nothing */
2725b6d0e7eSmrg }
2735b6d0e7eSmrg 
27461f28255Scgd /*
27561f28255Scgd  * Print a warning message if there is no daemon present.
27661f28255Scgd  */
2772847add2Scgd void
nodaemon(void)278895dc72aSwiz nodaemon(void)
27961f28255Scgd {
280e6a91a09Smrg 	if (remote)
28161f28255Scgd 		printf("\n%s: ", host);
28261f28255Scgd 	puts("Warning: no daemon present");
28361f28255Scgd 	current[0] = '\0';
28461f28255Scgd }
28561f28255Scgd 
28661f28255Scgd /*
28761f28255Scgd  * Print the header for the short listing format
28861f28255Scgd  */
2892847add2Scgd void
header(void)290895dc72aSwiz header(void)
29161f28255Scgd {
29261f28255Scgd 	printf(head0);
29361f28255Scgd 	col = strlen(head0)+1;
29461f28255Scgd 	blankfill(SIZCOL);
29561f28255Scgd 	printf(head1);
29661f28255Scgd }
29761f28255Scgd 
2982847add2Scgd void
inform(const char * cf)29904723c3fSchristos inform(const char *cf)
30061f28255Scgd {
301fe7ed7ceSmrg 	int j;
30261f28255Scgd 	FILE *cfp;
30361f28255Scgd 
30461f28255Scgd 	/*
30561f28255Scgd 	 * There's a chance the control file has gone away
30661f28255Scgd 	 * in the meantime; if this is the case just keep going
30761f28255Scgd 	 */
3088e41ca80Shpeyerl 	seteuid(euid);
30961f28255Scgd 	if ((cfp = fopen(cf, "r")) == NULL)
31061f28255Scgd 		return;
3118e41ca80Shpeyerl 	seteuid(uid);
31261f28255Scgd 
31361f28255Scgd 	if (rank < 0)
31461f28255Scgd 		rank = 0;
315e6a91a09Smrg 	if (remote || garbage || strcmp(cf, current))
31661f28255Scgd 		rank++;
31761f28255Scgd 	j = 0;
318*7027866aSroy 	while (get_line(cfp)) {
31961f28255Scgd 		switch (line[0]) {
32061f28255Scgd 		case 'P': /* Was this file specified in the user's list? */
32161f28255Scgd 			if (!inlist(line+1, cf)) {
32261f28255Scgd 				fclose(cfp);
32361f28255Scgd 				return;
32461f28255Scgd 			}
32561f28255Scgd 			if (lflag) {
32661f28255Scgd 				printf("\n%s: ", line+1);
32761f28255Scgd 				col = strlen(line+1) + 2;
32861f28255Scgd 				prank(rank);
32961f28255Scgd 				blankfill(JOBCOL);
33061f28255Scgd 				printf(" [job %s]\n", cf+3);
33161f28255Scgd 			} else {
33261f28255Scgd 				col = 0;
33361f28255Scgd 				prank(rank);
33461f28255Scgd 				blankfill(OWNCOL);
33561f28255Scgd 				printf("%-10s %-3d  ", line+1, atoi(cf+3));
33661f28255Scgd 				col += 16;
33761f28255Scgd 				first = 1;
33861f28255Scgd 			}
33961f28255Scgd 			continue;
34061f28255Scgd 		default: /* some format specifer and file name? */
34161f28255Scgd 			if (line[0] < 'a' || line[0] > 'z')
34261f28255Scgd 				continue;
34304723c3fSchristos 			if (j == 0 || strcmp(fname, line+1) != 0) {
34404723c3fSchristos 				(void)strlcpy(fname, line+1, sizeof(fname));
345fe7ed7ceSmrg 			}
34661f28255Scgd 			j++;
34761f28255Scgd 			continue;
34861f28255Scgd 		case 'N':
34904723c3fSchristos 			show(line + 1, fname, j);
35004723c3fSchristos 			fname[0] = '\0';
35161f28255Scgd 			j = 0;
35261f28255Scgd 		}
35361f28255Scgd 	}
35461f28255Scgd 	fclose(cfp);
35561f28255Scgd 	if (!lflag) {
35661f28255Scgd 		blankfill(SIZCOL);
35761f28255Scgd 		printf("%ld bytes\n", totsize);
35861f28255Scgd 		totsize = 0;
35961f28255Scgd 	}
36061f28255Scgd }
36161f28255Scgd 
3622847add2Scgd int
inlist(const char * name,const char * file)36304723c3fSchristos inlist(const char *name, const char *file)
36461f28255Scgd {
365fe7ed7ceSmrg 	int *r, n;
36604723c3fSchristos 	char **u;
36704723c3fSchristos 	const char *cp;
36861f28255Scgd 
36961f28255Scgd 	if (users == 0 && requests == 0)
37061f28255Scgd 		return(1);
37161f28255Scgd 	/*
37261f28255Scgd 	 * Check to see if it's in the user list
37361f28255Scgd 	 */
37461f28255Scgd 	for (u = user; u < &user[users]; u++)
37561f28255Scgd 		if (!strcmp(*u, name))
37661f28255Scgd 			return(1);
37761f28255Scgd 	/*
37861f28255Scgd 	 * Check the request list
37961f28255Scgd 	 */
38075ba9fc7Sdsl 	for (n = 0, cp = file+3; isdigit((unsigned char)*cp); )
38161f28255Scgd 		n = n * 10 + (*cp++ - '0');
38261f28255Scgd 	for (r = requ; r < &requ[requests]; r++)
38361f28255Scgd 		if (*r == n && !strcmp(cp, from))
38461f28255Scgd 			return(1);
38561f28255Scgd 	return(0);
38661f28255Scgd }
38761f28255Scgd 
3882847add2Scgd void
show(const char * nfile,const char * file,int copies)38904723c3fSchristos show(const char *nfile, const char *file, int copies)
39061f28255Scgd {
39161f28255Scgd 	if (strcmp(nfile, " ") == 0)
39261f28255Scgd 		nfile = "(standard input)";
39361f28255Scgd 	if (lflag)
39461f28255Scgd 		ldump(nfile, file, copies);
39561f28255Scgd 	else
39661f28255Scgd 		dump(nfile, file, copies);
39761f28255Scgd }
39861f28255Scgd 
39961f28255Scgd /*
40061f28255Scgd  * Fill the line with blanks to the specified column
40161f28255Scgd  */
4022847add2Scgd void
blankfill(int n)403895dc72aSwiz blankfill(int n)
40461f28255Scgd {
40561f28255Scgd 	while (col++ < n)
40661f28255Scgd 		putchar(' ');
40761f28255Scgd }
40861f28255Scgd 
40961f28255Scgd /*
41061f28255Scgd  * Give the abbreviated dump of the file names
41161f28255Scgd  */
4122847add2Scgd void
dump(const char * nfile,const char * file,int copies)41304723c3fSchristos dump(const char *nfile, const char *file, int copies)
41461f28255Scgd {
415fe7ed7ceSmrg 	short n, fill;
41661f28255Scgd 	struct stat lbuf;
41761f28255Scgd 
41861f28255Scgd 	/*
41961f28255Scgd 	 * Print as many files as will fit
42061f28255Scgd 	 *  (leaving room for the total size)
42161f28255Scgd 	 */
42261f28255Scgd 	 fill = first ? 0 : 2;	/* fill space for ``, '' */
42361f28255Scgd 	 if (((n = strlen(nfile)) + col + fill) >= SIZCOL-4) {
42461f28255Scgd 		if (col < SIZCOL) {
42561f28255Scgd 			printf(" ..."), col += 4;
42661f28255Scgd 			blankfill(SIZCOL);
42761f28255Scgd 		}
42861f28255Scgd 	} else {
42961f28255Scgd 		if (first)
43061f28255Scgd 			first = 0;
43161f28255Scgd 		else
43261f28255Scgd 			printf(", ");
43361f28255Scgd 		printf("%s", nfile);
43461f28255Scgd 		col += n+fill;
43561f28255Scgd 	}
4368e41ca80Shpeyerl 	seteuid(euid);
43761f28255Scgd 	if (*file && !stat(file, &lbuf))
43823b9fac0Smrg 		totsize += copies * (long)lbuf.st_size;
4398e41ca80Shpeyerl 	seteuid(uid);
44061f28255Scgd }
44161f28255Scgd 
44261f28255Scgd /*
44361f28255Scgd  * Print the long info about the file
44461f28255Scgd  */
4452847add2Scgd void
ldump(const char * nfile,const char * file,int copies)44604723c3fSchristos ldump(const char *nfile, const char *file, int copies)
44761f28255Scgd {
44861f28255Scgd 	struct stat lbuf;
44961f28255Scgd 
45061f28255Scgd 	putchar('\t');
45161f28255Scgd 	if (copies > 1)
45261f28255Scgd 		printf("%-2d copies of %-19s", copies, nfile);
45361f28255Scgd 	else
45461f28255Scgd 		printf("%-32s", nfile);
45561f28255Scgd 	if (*file && !stat(file, &lbuf))
456c2aa46e7Slukem 		printf(" %lld bytes", (long long)lbuf.st_size);
45761f28255Scgd 	else
45861f28255Scgd 		printf(" ??? bytes");
45961f28255Scgd 	putchar('\n');
46061f28255Scgd }
46161f28255Scgd 
46261f28255Scgd /*
46361f28255Scgd  * Print the job's rank in the queue,
46461f28255Scgd  *   update col for screen management
46561f28255Scgd  */
4662847add2Scgd void
prank(int n)467895dc72aSwiz prank(int n)
46861f28255Scgd {
4692847add2Scgd 	char rline[100];
47004723c3fSchristos 	static const char *r[] = {
47161f28255Scgd 		"th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
47261f28255Scgd 	};
47361f28255Scgd 
47461f28255Scgd 	if (n == 0) {
47561f28255Scgd 		printf("active");
47661f28255Scgd 		col += 6;
47761f28255Scgd 		return;
47861f28255Scgd 	}
47961f28255Scgd 	if ((n/10)%10 == 1)
4802847add2Scgd 		(void)snprintf(rline, sizeof(rline), "%dth", n);
48161f28255Scgd 	else
4822847add2Scgd 		(void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
4832847add2Scgd 	col += strlen(rline);
4842847add2Scgd 	printf("%s", rline);
48561f28255Scgd }
486