displayq.c revision 79739
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1983, 1993 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * Redistribution and use in source and binary forms, with or without 61553Srgrimes * modification, are permitted provided that the following conditions 71553Srgrimes * are met: 81553Srgrimes * 1. Redistributions of source code must retain the above copyright 91553Srgrimes * notice, this list of conditions and the following disclaimer. 101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111553Srgrimes * notice, this list of conditions and the following disclaimer in the 121553Srgrimes * documentation and/or other materials provided with the distribution. 131553Srgrimes * 3. All advertising materials mentioning features or use of this software 141553Srgrimes * must display the following acknowledgement: 151553Srgrimes * This product includes software developed by the University of 161553Srgrimes * California, Berkeley and its contributors. 171553Srgrimes * 4. Neither the name of the University nor the names of its contributors 181553Srgrimes * may be used to endorse or promote products derived from this software 191553Srgrimes * without specific prior written permission. 201553Srgrimes * 211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311553Srgrimes * SUCH DAMAGE. 321553Srgrimes */ 331553Srgrimes 341553Srgrimes#ifndef lint 3531492Swollman/* 3615648Sjoergstatic char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95"; 3731492Swollman*/ 3831492Swollmanstatic const char rcsid[] = 3950479Speter "$FreeBSD: head/usr.sbin/lpr/common_source/displayq.c 79739 2001-07-15 00:09:46Z gad $"; 401553Srgrimes#endif /* not lint */ 411553Srgrimes 421553Srgrimes#include <sys/param.h> 431553Srgrimes#include <sys/stat.h> 441553Srgrimes 4531492Swollman#include <ctype.h> 4631492Swollman#include <dirent.h> 4731492Swollman#include <errno.h> 4831492Swollman#include <fcntl.h> 491553Srgrimes#include <signal.h> 501553Srgrimes#include <stdio.h> 511553Srgrimes#include <stdlib.h> 521553Srgrimes#include <string.h> 5331492Swollman#define psignal foil_gcc_psignal 5431492Swollman#define sys_siglist foil_gcc_siglist 5531492Swollman#include <unistd.h> 5631492Swollman#undef psignal 5731492Swollman#undef sys_siglist 5831492Swollman 591553Srgrimes#include "lp.h" 601553Srgrimes#include "lp.local.h" 611553Srgrimes#include "pathnames.h" 621553Srgrimes 631553Srgrimes/* 641553Srgrimes * Routines to display the state of the queue. 651553Srgrimes */ 661553Srgrimes#define JOBCOL 40 /* column for job # in -l format */ 671553Srgrimes#define OWNCOL 7 /* start of Owner column in normal */ 681553Srgrimes#define SIZCOL 62 /* start of Size column in normal */ 691553Srgrimes 701553Srgrimes/* 711553Srgrimes * Stuff for handling job specifications 721553Srgrimes */ 7327618Simpextern uid_t uid, euid; 7427618Simp 751553Srgrimesstatic int col; /* column on screen */ 761553Srgrimesstatic char current[40]; /* current file being printed */ 771553Srgrimesstatic char file[132]; /* print file name */ 781553Srgrimesstatic int first; /* first file in ``files'' column? */ 791553Srgrimesstatic int garbage; /* # of garbage cf files */ 801553Srgrimesstatic int lflag; /* long output option */ 811553Srgrimesstatic int rank; /* order to be printed (-1=none, 0=active) */ 821553Srgrimesstatic long totsize; /* total print job size in bytes */ 831553Srgrimes 8478146Sgadstatic const char *head0 = "Rank Owner Job Files"; 8578146Sgadstatic const char *head1 = "Total Size\n"; 861553Srgrimes 8778146Sgadstatic void alarmhandler(int _signo); 8878146Sgadstatic void warn(const struct printer *_pp); 8926844Sjoerg 901553Srgrimes/* 911553Srgrimes * Display the current state of the queue. Format = 1 if long format. 921553Srgrimes */ 931553Srgrimesvoid 9478146Sgaddisplayq(struct printer *pp, int format) 951553Srgrimes{ 9668401Sgad register struct jobqueue *q; 9727618Simp register int i, nitems, fd, ret; 981553Srgrimes register char *cp; 9968401Sgad struct jobqueue **queue; 1001553Srgrimes struct stat statb; 1011553Srgrimes FILE *fp; 10226844Sjoerg void (*savealrm)(int); 1031553Srgrimes 1041553Srgrimes lflag = format; 1051553Srgrimes totsize = 0; 1061553Srgrimes rank = -1; 10731492Swollman 10831492Swollman if ((cp = checkremote(pp))) { 1091553Srgrimes printf("Warning: %s\n", cp); 11031492Swollman free(cp); 11131492Swollman } 1121553Srgrimes 1131553Srgrimes /* 1141553Srgrimes * Print out local queue 1151553Srgrimes * Find all the control files in the spooling directory 1161553Srgrimes */ 11727618Simp seteuid(euid); 11831492Swollman if (chdir(pp->spool_dir) < 0) 11931492Swollman fatal(pp, "cannot chdir to spooling directory: %s", 12031492Swollman strerror(errno)); 12127618Simp seteuid(uid); 12231492Swollman if ((nitems = getq(pp, &queue)) < 0) 12331492Swollman fatal(pp, "cannot examine spooling area\n"); 12427618Simp seteuid(euid); 12531492Swollman ret = stat(pp->lock_file, &statb); 12627618Simp seteuid(uid); 12727618Simp if (ret >= 0) { 12831492Swollman if (statb.st_mode & LFM_PRINT_DIS) { 12931492Swollman if (pp->remote) 13078300Sgad printf("%s: ", local_host); 13131492Swollman printf("Warning: %s is down: ", pp->printer); 13227618Simp seteuid(euid); 13331492Swollman fd = open(pp->status_file, O_RDONLY|O_SHLOCK); 13427618Simp seteuid(uid); 1351553Srgrimes if (fd >= 0) { 1361553Srgrimes while ((i = read(fd, line, sizeof(line))) > 0) 1371553Srgrimes (void) fwrite(line, 1, i, stdout); 1381553Srgrimes (void) close(fd); /* unlocks as well */ 1391553Srgrimes } else 1401553Srgrimes putchar('\n'); 1411553Srgrimes } 14231492Swollman if (statb.st_mode & LFM_QUEUE_DIS) { 14331492Swollman if (pp->remote) 14478300Sgad printf("%s: ", local_host); 14531492Swollman printf("Warning: %s queue is turned off\n", 14631492Swollman pp->printer); 1471553Srgrimes } 1481553Srgrimes } 1491553Srgrimes 1501553Srgrimes if (nitems) { 15127618Simp seteuid(euid); 15231492Swollman fp = fopen(pp->lock_file, "r"); 15327618Simp seteuid(uid); 1541553Srgrimes if (fp == NULL) 15531492Swollman warn(pp); 1561553Srgrimes else { 1571553Srgrimes /* get daemon pid */ 1581553Srgrimes cp = current; 15915648Sjoerg while ((i = getc(fp)) != EOF && i != '\n') 16015648Sjoerg *cp++ = i; 1611553Srgrimes *cp = '\0'; 1621553Srgrimes i = atoi(current); 16327618Simp if (i <= 0) { 16427618Simp ret = -1; 16527618Simp } else { 16627618Simp seteuid(euid); 16727618Simp ret = kill(i, 0); 16827618Simp seteuid(uid); 16927618Simp } 17027618Simp if (ret < 0) { 17131492Swollman warn(pp); 17227618Simp } else { 1731553Srgrimes /* read current file name */ 1741553Srgrimes cp = current; 17515648Sjoerg while ((i = getc(fp)) != EOF && i != '\n') 17615648Sjoerg *cp++ = i; 1771553Srgrimes *cp = '\0'; 1781553Srgrimes /* 1791553Srgrimes * Print the status file. 1801553Srgrimes */ 18131492Swollman if (pp->remote) 18278300Sgad printf("%s: ", local_host); 18327618Simp seteuid(euid); 18431492Swollman fd = open(pp->status_file, O_RDONLY|O_SHLOCK); 18527618Simp seteuid(uid); 1861553Srgrimes if (fd >= 0) { 18731492Swollman while ((i = read(fd, line, 18831492Swollman sizeof(line))) > 0) 18931492Swollman fwrite(line, 1, i, stdout); 19031492Swollman close(fd); /* unlocks as well */ 1911553Srgrimes } else 1921553Srgrimes putchar('\n'); 1931553Srgrimes } 1941553Srgrimes (void) fclose(fp); 1951553Srgrimes } 1961553Srgrimes /* 1971553Srgrimes * Now, examine the control files and print out the jobs to 1981553Srgrimes * be done for each user. 1991553Srgrimes */ 2001553Srgrimes if (!lflag) 2011553Srgrimes header(); 2021553Srgrimes for (i = 0; i < nitems; i++) { 2031553Srgrimes q = queue[i]; 20468401Sgad inform(pp, q->job_cfname); 2051553Srgrimes free(q); 2061553Srgrimes } 2071553Srgrimes free(queue); 2081553Srgrimes } 20931492Swollman if (!pp->remote) { 2101553Srgrimes if (nitems == 0) 2111553Srgrimes puts("no entries"); 2121553Srgrimes return; 2131553Srgrimes } 2141553Srgrimes 2151553Srgrimes /* 2161553Srgrimes * Print foreign queue 2171553Srgrimes * Note that a file in transit may show up in either queue. 2181553Srgrimes */ 2191553Srgrimes if (nitems) 2201553Srgrimes putchar('\n'); 22131492Swollman (void) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3', 22231492Swollman pp->remote_queue); 2231553Srgrimes cp = line; 22427757Simp for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) { 2251553Srgrimes cp += strlen(cp); 2261553Srgrimes (void) sprintf(cp, " %d", requ[i]); 2271553Srgrimes } 22822466Simp for (i = 0; i < users && cp - line + 1 + strlen(user[i]) < 22927757Simp sizeof(line) - 1; i++) { 2301553Srgrimes cp += strlen(cp); 2311553Srgrimes *cp++ = ' '; 2321553Srgrimes (void) strcpy(cp, user[i]); 2331553Srgrimes } 2341553Srgrimes strcat(line, "\n"); 23526844Sjoerg savealrm = signal(SIGALRM, alarmhandler); 23631492Swollman alarm(pp->conn_timeout); 23731492Swollman fd = getport(pp, pp->remote_host, 0); 23831020Sjoerg alarm(0); 23926844Sjoerg (void)signal(SIGALRM, savealrm); 2401553Srgrimes if (fd < 0) { 24178300Sgad if (from_host != local_host) 24278300Sgad printf("%s: ", local_host); 24331492Swollman printf("connection to %s is down\n", pp->remote_host); 2441553Srgrimes } 2451553Srgrimes else { 2461553Srgrimes i = strlen(line); 2471553Srgrimes if (write(fd, line, i) != i) 24831492Swollman fatal(pp, "Lost connection"); 2491553Srgrimes while ((i = read(fd, line, sizeof(line))) > 0) 2501553Srgrimes (void) fwrite(line, 1, i, stdout); 2511553Srgrimes (void) close(fd); 2521553Srgrimes } 2531553Srgrimes} 2541553Srgrimes 2551553Srgrimes/* 2561553Srgrimes * Print a warning message if there is no daemon present. 2571553Srgrimes */ 25828621Sjoergstatic void 25978146Sgadwarn(const struct printer *pp) 2601553Srgrimes{ 26131492Swollman if (pp->remote) 26278300Sgad printf("%s: ", local_host); 2631553Srgrimes puts("Warning: no daemon present"); 2641553Srgrimes current[0] = '\0'; 2651553Srgrimes} 2661553Srgrimes 2671553Srgrimes/* 2681553Srgrimes * Print the header for the short listing format 2691553Srgrimes */ 2701553Srgrimesvoid 27178146Sgadheader(void) 2721553Srgrimes{ 27379739Sgad printf("%s", head0); 2741553Srgrimes col = strlen(head0)+1; 2751553Srgrimes blankfill(SIZCOL); 27679739Sgad printf("%s", head1); 2771553Srgrimes} 2781553Srgrimes 2791553Srgrimesvoid 28078146Sgadinform(const struct printer *pp, char *cf) 2811553Srgrimes{ 28268100Sgad register int copycnt; 28368100Sgad char savedname[MAXPATHLEN+1]; 28468100Sgad FILE *cfp; 2851553Srgrimes 2861553Srgrimes /* 2871553Srgrimes * There's a chance the control file has gone away 2881553Srgrimes * in the meantime; if this is the case just keep going 2891553Srgrimes */ 29027618Simp seteuid(euid); 2911553Srgrimes if ((cfp = fopen(cf, "r")) == NULL) 2921553Srgrimes return; 29327618Simp seteuid(uid); 2941553Srgrimes 2951553Srgrimes if (rank < 0) 2961553Srgrimes rank = 0; 29731492Swollman if (pp->remote || garbage || strcmp(cf, current)) 2981553Srgrimes rank++; 29968100Sgad 30068100Sgad /* 30168100Sgad * The cf-file may include commands to print more than one datafile 30268100Sgad * from the user. For each datafile, the cf-file contains at least 30368100Sgad * one line which starts with some format-specifier ('a'-'z'), and 30468100Sgad * a second line ('N'ame) which indicates the original name the user 30568100Sgad * specified for that file. There can be multiple format-spec lines 30668100Sgad * for a single Name-line, if the user requested multiple copies of 30768100Sgad * that file. Standard lpr puts the format-spec line(s) before the 30868100Sgad * Name-line, while lprNG puts the Name-line before the format-spec 30968100Sgad * line(s). This section needs to handle the lines in either order. 31068100Sgad */ 31168100Sgad copycnt = 0; 31268100Sgad file[0] = '\0'; 31368100Sgad savedname[0] = '\0'; 3141553Srgrimes while (getline(cfp)) { 3151553Srgrimes switch (line[0]) { 3161553Srgrimes case 'P': /* Was this file specified in the user's list? */ 3171553Srgrimes if (!inlist(line+1, cf)) { 3181553Srgrimes fclose(cfp); 3191553Srgrimes return; 3201553Srgrimes } 3211553Srgrimes if (lflag) { 3221553Srgrimes printf("\n%s: ", line+1); 3231553Srgrimes col = strlen(line+1) + 2; 3241553Srgrimes prank(rank); 3251553Srgrimes blankfill(JOBCOL); 3261553Srgrimes printf(" [job %s]\n", cf+3); 3271553Srgrimes } else { 3281553Srgrimes col = 0; 3291553Srgrimes prank(rank); 3301553Srgrimes blankfill(OWNCOL); 3311553Srgrimes printf("%-10s %-3d ", line+1, atoi(cf+3)); 3321553Srgrimes col += 16; 3331553Srgrimes first = 1; 3341553Srgrimes } 3351553Srgrimes continue; 3361553Srgrimes default: /* some format specifer and file name? */ 3371553Srgrimes if (line[0] < 'a' || line[0] > 'z') 33868100Sgad break; 33968100Sgad if (copycnt == 0 || strcmp(file, line+1) != 0) { 34068100Sgad strncpy(file, line + 1, sizeof(file) - 1); 34122466Simp file[sizeof(file) - 1] = '\0'; 34222466Simp } 34368100Sgad copycnt++; 34468100Sgad /* 34568100Sgad * deliberately 'continue' to another getline(), so 34668100Sgad * all format-spec lines for this datafile are read 34768100Sgad * in and counted before calling show() 34868100Sgad */ 3491553Srgrimes continue; 3501553Srgrimes case 'N': 35168100Sgad strncpy(savedname, line + 1, sizeof(savedname) - 1); 35268100Sgad savedname[sizeof(savedname) - 1] = '\0'; 35368100Sgad break; 35468100Sgad } 35568100Sgad if ((file[0] != '\0') && (savedname[0] != '\0')) { 35668100Sgad show(savedname, file, copycnt); 35768100Sgad copycnt = 0; 3581553Srgrimes file[0] = '\0'; 35968100Sgad savedname[0] = '\0'; 3601553Srgrimes } 3611553Srgrimes } 3621553Srgrimes fclose(cfp); 36368100Sgad /* check for a file which hasn't been shown yet */ 36468100Sgad if (file[0] != '\0') { 36568100Sgad if (savedname[0] == '\0') { 36668100Sgad /* a safeguard in case the N-ame line is missing */ 36768100Sgad strncpy(savedname, file, sizeof(savedname) - 1); 36868100Sgad savedname[sizeof(savedname) - 1] = '\0'; 36968100Sgad } 37068100Sgad show(savedname, file, copycnt); 37168100Sgad } 3721553Srgrimes if (!lflag) { 3731553Srgrimes blankfill(SIZCOL); 3741553Srgrimes printf("%ld bytes\n", totsize); 3751553Srgrimes totsize = 0; 3761553Srgrimes } 3771553Srgrimes} 3781553Srgrimes 3791553Srgrimesint 38078146Sgadinlist(char *uname, char *cfile) 3811553Srgrimes{ 3821553Srgrimes register int *r, n; 3831553Srgrimes register char **u, *cp; 3841553Srgrimes 3851553Srgrimes if (users == 0 && requests == 0) 3861553Srgrimes return(1); 3871553Srgrimes /* 3881553Srgrimes * Check to see if it's in the user list 3891553Srgrimes */ 3901553Srgrimes for (u = user; u < &user[users]; u++) 39178146Sgad if (!strcmp(*u, uname)) 3921553Srgrimes return(1); 3931553Srgrimes /* 3941553Srgrimes * Check the request list 3951553Srgrimes */ 39678146Sgad for (n = 0, cp = cfile+3; isdigit(*cp); ) 3971553Srgrimes n = n * 10 + (*cp++ - '0'); 3981553Srgrimes for (r = requ; r < &requ[requests]; r++) 39978300Sgad if (*r == n && !strcmp(cp, from_host)) 4001553Srgrimes return(1); 4011553Srgrimes return(0); 4021553Srgrimes} 4031553Srgrimes 4041553Srgrimesvoid 40578146Sgadshow(const char *nfile, const char *datafile, int copies) 4061553Srgrimes{ 4071553Srgrimes if (strcmp(nfile, " ") == 0) 4081553Srgrimes nfile = "(standard input)"; 4091553Srgrimes if (lflag) 41078146Sgad ldump(nfile, datafile, copies); 4111553Srgrimes else 41278146Sgad dump(nfile, datafile, copies); 4131553Srgrimes} 4141553Srgrimes 4151553Srgrimes/* 4161553Srgrimes * Fill the line with blanks to the specified column 4171553Srgrimes */ 4181553Srgrimesvoid 41978146Sgadblankfill(int tocol) 4201553Srgrimes{ 42178146Sgad while (col++ < tocol) 4221553Srgrimes putchar(' '); 4231553Srgrimes} 4241553Srgrimes 4251553Srgrimes/* 4261553Srgrimes * Give the abbreviated dump of the file names 4271553Srgrimes */ 4281553Srgrimesvoid 42978146Sgaddump(const char *nfile, const char *datafile, int copies) 4301553Srgrimes{ 4311553Srgrimes struct stat lbuf; 43268101Sgad const char etctmpl[] = ", ..."; 43368101Sgad char etc[sizeof(etctmpl)]; 43468101Sgad char *lastsep; 43568101Sgad short fill, nlen; 43668101Sgad short rem, remetc; 4371553Srgrimes 4381553Srgrimes /* 43968101Sgad * Print as many filenames as will fit 44068101Sgad * (leaving room for the 'total size' field) 4411553Srgrimes */ 44268101Sgad fill = first ? 0 : 2; /* fill space for ``, '' */ 44368101Sgad nlen = strlen(nfile); 44468101Sgad rem = SIZCOL - 1 - col; 44568101Sgad if (nlen + fill > rem) { 44668101Sgad if (first) { 44768101Sgad /* print the right-most part of the name */ 44868101Sgad printf("...%s ", &nfile[3+nlen-rem]); 44968101Sgad col = SIZCOL; 45068101Sgad } else if (rem > 0) { 45168101Sgad /* fit as much of the etc-string as we can */ 45268101Sgad remetc = rem; 45368101Sgad if (rem > strlen(etctmpl)) 45468101Sgad remetc = strlen(etctmpl); 45568101Sgad etc[0] = '\0'; 45668101Sgad strncat(etc, etctmpl, remetc); 45779739Sgad printf("%s", etc); 45868101Sgad col += remetc; 45968101Sgad rem -= remetc; 46068101Sgad /* room for the last segment of this filename? */ 46168101Sgad lastsep = strrchr(nfile, '/'); 46268101Sgad if ((lastsep != NULL) && (rem > strlen(lastsep))) { 46368101Sgad /* print the right-most part of this name */ 46468101Sgad printf("%s", lastsep); 46568101Sgad col += strlen(lastsep); 46668101Sgad } else { 46768101Sgad /* do not pack any more names in here */ 46868101Sgad blankfill(SIZCOL); 46968101Sgad } 4701553Srgrimes } 4711553Srgrimes } else { 47268101Sgad if (!first) 4731553Srgrimes printf(", "); 4741553Srgrimes printf("%s", nfile); 47568101Sgad col += nlen + fill; 4761553Srgrimes } 47768101Sgad first = 0; 47868101Sgad 47927618Simp seteuid(euid); 48078146Sgad if (*datafile && !stat(datafile, &lbuf)) 4811553Srgrimes totsize += copies * lbuf.st_size; 48227618Simp seteuid(uid); 4831553Srgrimes} 4841553Srgrimes 4851553Srgrimes/* 4861553Srgrimes * Print the long info about the file 4871553Srgrimes */ 4881553Srgrimesvoid 48978146Sgadldump(const char *nfile, const char *datafile, int copies) 4901553Srgrimes{ 4911553Srgrimes struct stat lbuf; 4921553Srgrimes 4931553Srgrimes putchar('\t'); 4941553Srgrimes if (copies > 1) 4951553Srgrimes printf("%-2d copies of %-19s", copies, nfile); 4961553Srgrimes else 4971553Srgrimes printf("%-32s", nfile); 49878146Sgad if (*datafile && !stat(datafile, &lbuf)) 49935998Sjb printf(" %qd bytes", (long long) lbuf.st_size); 5001553Srgrimes else 5011553Srgrimes printf(" ??? bytes"); 5021553Srgrimes putchar('\n'); 5031553Srgrimes} 5041553Srgrimes 5051553Srgrimes/* 5061553Srgrimes * Print the job's rank in the queue, 5071553Srgrimes * update col for screen management 5081553Srgrimes */ 5091553Srgrimesvoid 51078146Sgadprank(int n) 5111553Srgrimes{ 5121553Srgrimes char rline[100]; 51378146Sgad static const char *r[] = { 5141553Srgrimes "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" 5151553Srgrimes }; 5161553Srgrimes 5171553Srgrimes if (n == 0) { 5181553Srgrimes printf("active"); 5191553Srgrimes col += 6; 5201553Srgrimes return; 5211553Srgrimes } 5221553Srgrimes if ((n/10)%10 == 1) 5231553Srgrimes (void)snprintf(rline, sizeof(rline), "%dth", n); 5241553Srgrimes else 5251553Srgrimes (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]); 5261553Srgrimes col += strlen(rline); 5271553Srgrimes printf("%s", rline); 5281553Srgrimes} 52926844Sjoerg 53026844Sjoergvoid 53178146Sgadalarmhandler(int signo __unused) 53226844Sjoerg{ 53378146Sgad /* the signal is ignored */ 53478146Sgad /* (the '__unused' is just to avoid a compile-time warning) */ 53526844Sjoerg} 536