4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / lib / libc / net / res_debug.c
CommitLineData
00976ca1 1/*-
67039e6d
KB
2 * Copyright (c) 1985, 1990, 1993
3 * The Regents of the University of California. All rights reserved.
6b2f9dd0 4 *
00976ca1 5 * %sccs.include.redist.c%
882ceeb2
MK
6 * -
7 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies, and that
12 * the name of Digital Equipment Corporation not be used in advertising or
13 * publicity pertaining to distribution of the document or software without
14 * specific, written prior permission.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
17 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
19 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
20 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
21 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
22 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 * SOFTWARE.
24 * -
25 * --Copyright--
b423e985
RC
26 */
27
2ce81398 28#if defined(LIBC_SCCS) && !defined(lint)
67039e6d 29static char sccsid[] = "@(#)res_debug.c 8.1 (Berkeley) %G%";
6b2f9dd0 30#endif /* LIBC_SCCS and not lint */
8ea4199d 31
7596199c 32#include <sys/param.h>
0d22afd1 33#include <netinet/in.h>
24fac7d8 34#include <arpa/inet.h>
02a51825 35#include <arpa/nameser.h>
24fac7d8
KB
36#include <resolv.h>
37#include <stdio.h>
38#include <string.h>
0d22afd1 39
7624baca 40void __fp_query();
16dfe61e 41char *__p_class(), *__p_time(), *__p_type();
882ceeb2
MK
42char *p_cdname(), *p_fqname(), *p_rr();
43static char *p_option __P((u_int32_t));
0d22afd1 44
cd43bd6a 45char *_res_opcodes[] = {
0d22afd1
RC
46 "QUERY",
47 "IQUERY",
48 "CQUERYM",
49 "CQUERYU",
50 "4",
51 "5",
52 "6",
53 "7",
54 "8",
0d22afd1
RC
55 "UPDATEA",
56 "UPDATED",
3e8361d9 57 "UPDATEDA",
0d22afd1 58 "UPDATEM",
3e8361d9 59 "UPDATEMA",
0d22afd1
RC
60 "ZONEINIT",
61 "ZONEREF",
62};
63
cd43bd6a 64char *_res_resultcodes[] = {
0d22afd1
RC
65 "NOERROR",
66 "FORMERR",
67 "SERVFAIL",
68 "NXDOMAIN",
69 "NOTIMP",
70 "REFUSED",
71 "6",
72 "7",
73 "8",
74 "9",
75 "10",
76 "11",
77 "12",
78 "13",
79 "14",
80 "NOCHANGE",
81};
82
882ceeb2
MK
83static char retbuf[16];
84
85static char *
86dewks(wks)
87 int wks;
88{
89 switch (wks) {
90 case 5: return("rje");
91 case 7: return("echo");
92 case 9: return("discard");
93 case 11: return("systat");
94 case 13: return("daytime");
95 case 15: return("netstat");
96 case 17: return("qotd");
97 case 19: return("chargen");
98 case 20: return("ftp-data");
99 case 21: return("ftp");
100 case 23: return("telnet");
101 case 25: return("smtp");
102 case 37: return("time");
103 case 39: return("rlp");
104 case 42: return("name");
105 case 43: return("whois");
106 case 53: return("domain");
107 case 57: return("apts");
108 case 59: return("apfs");
109 case 67: return("bootps");
110 case 68: return("bootpc");
111 case 69: return("tftp");
112 case 77: return("rje");
113 case 79: return("finger");
114 case 87: return("link");
115 case 95: return("supdup");
116 case 100: return("newacct");
117 case 101: return("hostnames");
118 case 102: return("iso-tsap");
119 case 103: return("x400");
120 case 104: return("x400-snd");
121 case 105: return("csnet-ns");
122 case 109: return("pop-2");
123 case 111: return("sunrpc");
124 case 113: return("auth");
125 case 115: return("sftp");
126 case 117: return("uucp-path");
127 case 119: return("nntp");
128 case 121: return("erpc");
129 case 123: return("ntp");
130 case 133: return("statsrv");
131 case 136: return("profile");
132 case 144: return("NeWS");
133 case 161: return("snmp");
134 case 162: return("snmp-trap");
135 case 170: return("print-srv");
136 default: (void) sprintf(retbuf, "%d", wks); return(retbuf);
137 }
138}
139
140static char *
141deproto(protonum)
142 int protonum;
143{
144 switch (protonum) {
145 case 1: return("icmp");
146 case 2: return("igmp");
147 case 3: return("ggp");
148 case 5: return("st");
149 case 6: return("tcp");
150 case 7: return("ucl");
151 case 8: return("egp");
152 case 9: return("igp");
153 case 11: return("nvp-II");
154 case 12: return("pup");
155 case 16: return("chaos");
156 case 17: return("udp");
157 default: (void) sprintf(retbuf, "%d", protonum); return(retbuf);
158 }
159}
160
161static char *
162do_rrset(msg, cp, cnt, pflag, file, hs)
163 int cnt, pflag;
164 char *cp,*msg, *hs;
165 FILE *file;
166{
167 int n;
168 int sflag;
169 /*
170 * Print answer records
171 */
172 sflag = (_res.pfcode & pflag);
173 if (n = ntohs(cnt)) {
174 if ((!_res.pfcode) || ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
175 fprintf(file, hs);
176 while (--n >= 0) {
177 cp = p_rr(cp, msg, file);
178 if ((cp-msg) > PACKETSZ)
179 return (NULL);
180 }
181 if ((!_res.pfcode) || ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
182 putc('\n', file);
183 }
184 return(cp);
185}
186
371d3c9b 187__p_query(msg)
a73d974c
KD
188 char *msg;
189{
882ceeb2
MK
190 __fp_query(msg, stdout);
191}
192
193/*
194 * Print the current options.
195 * This is intended to be primarily a debugging routine.
196 */
197void
198__fp_resstat(statp, file)
199 struct __res_state *statp;
200 FILE *file;
201{
202 int bit;
203
204 fprintf(file, ";; res options:");
205 if (!statp)
206 statp = &_res;
207 for (bit = 0; bit < 32; bit++) { /* XXX 32 - bad assumption! */
208 if (statp->options & (1<<bit))
209 fprintf(file, " %s", p_option(1<<bit));
210 }
211 putc('\n', file);
a73d974c
KD
212}
213
0d22afd1
RC
214/*
215 * Print the contents of a query.
216 * This is intended to be primarily a debugging routine.
217 */
c22d8692
KB
218void
219__fp_query(msg,file)
e515a559 220 char *msg;
a73d974c 221 FILE *file;
0d22afd1
RC
222{
223 register char *cp;
224 register HEADER *hp;
225 register int n;
226
227 /*
228 * Print header fields.
229 */
e515a559
RC
230 hp = (HEADER *)msg;
231 cp = msg + sizeof(HEADER);
882ceeb2
MK
232 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEADX) || hp->rcode) {
233 fprintf(file,";; ->>HEADER<<- opcode: %s, status: %s, id: %d",
234 _res_opcodes[hp->opcode],
235 _res_resultcodes[hp->rcode],
236 ntohs(hp->id));
237 putc('\n', file);
238 }
239 putc(';', file);
240 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD2)) {
241 fprintf(file,"; flags:");
242 if (hp->qr)
243 fprintf(file," qr");
244 if (hp->aa)
245 fprintf(file," aa");
246 if (hp->tc)
247 fprintf(file," tc");
248 if (hp->rd)
249 fprintf(file," rd");
250 if (hp->ra)
251 fprintf(file," ra");
252 if (hp->pr)
253 fprintf(file," pr");
254 }
255 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD1)) {
256 fprintf(file,"; Ques: %d", ntohs(hp->qdcount));
257 fprintf(file,", Ans: %d", ntohs(hp->ancount));
258 fprintf(file,", Auth: %d", ntohs(hp->nscount));
259 fprintf(file,", Addit: %d\n", ntohs(hp->arcount));
260 }
261#if 0
262 if (_res.pfcode & (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1)) {
263 putc('\n',file);
264 }
265#endif
0d22afd1
RC
266 /*
267 * Print question records.
268 */
269 if (n = ntohs(hp->qdcount)) {
882ceeb2
MK
270 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
271 fprintf(file,";; QUESTIONS:\n");
0d22afd1 272 while (--n >= 0) {
882ceeb2 273 fprintf(file,";;\t");
a73d974c 274 cp = p_cdname(cp, msg, file);
0d22afd1
RC
275 if (cp == NULL)
276 return;
882ceeb2
MK
277 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
278 fprintf(file, ", type = %s",
279 __p_type(_getshort(cp)));
280 cp += sizeof(u_int16_t);
281 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
282 fprintf(file, ", class = %s\n\n",
283 __p_class(_getshort(cp)));
284 cp += sizeof(u_int16_t);
0d22afd1
RC
285 }
286 }
287 /*
288 * Print authoritative answer records
289 */
882ceeb2
MK
290 cp = do_rrset(msg, cp, hp->ancount, RES_PRF_ANS, file,
291 ";; ANSWERS:\n");
292 if (cp == NULL)
293 return;
294
0d22afd1
RC
295 /*
296 * print name server records
297 */
882ceeb2
MK
298 cp = do_rrset(msg, cp, hp->nscount, RES_PRF_AUTH, file,
299 ";; AUTHORITY RECORDS:\n");
300 if (!cp)
301 return;
302
0d22afd1
RC
303 /*
304 * print additional records
305 */
882ceeb2
MK
306 cp = do_rrset(msg, cp, hp->arcount, RES_PRF_ADD, file,
307 ";; ADDITIONAL RECORDS:\n");
308 if (!cp)
309 return;
0d22afd1
RC
310}
311
882ceeb2 312char *
a73d974c 313p_cdname(cp, msg, file)
e515a559 314 char *cp, *msg;
a73d974c 315 FILE *file;
0d22afd1
RC
316{
317 char name[MAXDNAME];
318 int n;
319
882ceeb2
MK
320 if ((n = dn_expand((u_char *)msg, (u_char *)msg + MAXCDNAME,
321 (u_char *)cp, (u_char *)name, sizeof(name))) < 0)
322 return (NULL);
323 if (name[0] == '\0')
324 putc('.', file);
325 else
326 fputs(name, file);
327 return (cp + n);
328}
329
330char *
331p_fqname(cp, msg, file)
332 char *cp, *msg;
333 FILE *file;
334{
335 char name[MAXDNAME];
336 int n, len;
337
338 if ((n = dn_expand((u_char *)msg, (u_char *)msg + MAXCDNAME,
339 (u_char *)cp, (u_char *)name, sizeof(name))) < 0)
0d22afd1
RC
340 return (NULL);
341 if (name[0] == '\0') {
882ceeb2
MK
342 putc('.', file);
343 } else {
344 fputs(name, file);
345 if (name[strlen(name) - 1] != '.')
346 putc('.', file);
0d22afd1 347 }
0d22afd1
RC
348 return (cp + n);
349}
350
351/*
352 * Print resource record fields in human readable form.
353 */
882ceeb2 354char *
a73d974c 355p_rr(cp, msg, file)
e515a559 356 char *cp, *msg;
a73d974c 357 FILE *file;
0d22afd1
RC
358{
359 int type, class, dlen, n, c;
360 struct in_addr inaddr;
4a7d845b 361 char *cp1, *cp2;
882ceeb2
MK
362 u_int32_t tmpttl, t;
363 int lcnt;
0d22afd1 364
882ceeb2 365 if ((cp = p_fqname(cp, msg, file)) == NULL)
0d22afd1 366 return (NULL); /* compression error */
882ceeb2
MK
367 type = _getshort(cp);
368 cp += sizeof(u_int16_t);
369 class = _getshort(cp);
370 cp += sizeof(u_int16_t);
371 tmpttl = _getlong(cp);
372 cp += sizeof(u_int32_t);
373 dlen = _getshort(cp);
374 cp += sizeof(u_int16_t);
0d22afd1 375 cp1 = cp;
882ceeb2
MK
376 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_TTLID))
377 fprintf(file, "\t%lu", tmpttl);
378 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_CLASS))
379 fprintf(file, "\t%s", __p_class(class));
380 fprintf(file, "\t%s", __p_type(type));
0d22afd1
RC
381 /*
382 * Print type specific data, if appropriate
383 */
384 switch (type) {
385 case T_A:
386 switch (class) {
387 case C_IN:
ad5ac231 388 case C_HS:
11e57e73 389 bcopy(cp, (char *)&inaddr, sizeof(inaddr));
0d22afd1 390 if (dlen == 4) {
882ceeb2 391 fprintf(file,"\t%s", inet_ntoa(inaddr));
0d22afd1
RC
392 cp += dlen;
393 } else if (dlen == 7) {
882ceeb2
MK
394 char *address;
395 u_char protocol;
396 u_short port;
397
398 address = inet_ntoa(inaddr);
399 cp += sizeof(inaddr);
400 protocol = *(u_char*)cp;
401 cp += sizeof(u_char);
402 port = _getshort(cp);
403 cp += sizeof(u_int16_t);
404 fprintf(file, "\t%s\t; proto %d, port %d",
405 address, protocol, port);
0d22afd1
RC
406 }
407 break;
e0dd8906
KD
408 default:
409 cp += dlen;
0d22afd1
RC
410 }
411 break;
412 case T_CNAME:
413 case T_MB:
0d22afd1
RC
414 case T_MG:
415 case T_MR:
416 case T_NS:
417 case T_PTR:
882ceeb2
MK
418 putc('\t', file);
419 cp = p_fqname(cp, msg, file);
0d22afd1
RC
420 break;
421
422 case T_HINFO:
423 if (n = *cp++) {
882ceeb2 424 fprintf(file,"\t%.*s", n, cp);
0d22afd1
RC
425 cp += n;
426 }
427 if (n = *cp++) {
882ceeb2 428 fprintf(file,"\t%.*s", n, cp);
0d22afd1
RC
429 cp += n;
430 }
431 break;
432
433 case T_SOA:
882ceeb2
MK
434 putc('\t', file);
435 cp = p_fqname(cp, msg, file); /* origin */
436 putc(' ', file);
437 cp = p_fqname(cp, msg, file); /* mail addr */
438 fputs(" (\n", file);
439 t = _getlong(cp); cp += sizeof(u_int32_t);
440 fprintf(file,"\t\t\t%lu\t; serial\n", t);
441 t = _getlong(cp); cp += sizeof(u_int32_t);
442 fprintf(file,"\t\t\t%lu\t; refresh (%s)\n", t, __p_time(t));
443 t = _getlong(cp); cp += sizeof(u_int32_t);
444 fprintf(file,"\t\t\t%lu\t; retry (%s)\n", t, __p_time(t));
445 t = _getlong(cp); cp += sizeof(u_int32_t);
446 fprintf(file,"\t\t\t%lu\t; expire (%s)\n", t, __p_time(t));
447 t = _getlong(cp); cp += sizeof(u_int32_t);
448 fprintf(file,"\t\t\t%lu )\t; minimum (%s)", t, __p_time(t));
0d22afd1
RC
449 break;
450
73546a32 451 case T_MX:
882ceeb2
MK
452 fprintf(file,"\t%d ", _getshort(cp));
453 cp += sizeof(u_int16_t);
454 cp = p_fqname(cp, msg, file);
73546a32
KD
455 break;
456
ad5ac231
JB
457 case T_TXT:
458 (void) fputs("\t\"", file);
459 cp2 = cp1 + dlen;
460 while (cp < cp2) {
461 if (n = (unsigned char) *cp++) {
462 for (c = n; c > 0 && cp < cp2; c--)
463 if (*cp == '\n') {
464 (void) putc('\\', file);
465 (void) putc(*cp++, file);
466 } else
467 (void) putc(*cp++, file);
468 }
469 }
882ceeb2 470 putc('"', file);
ad5ac231 471 break;
0af159f3 472
e515a559 473 case T_MINFO:
882ceeb2
MK
474 case T_RP:
475 putc('\t', file);
476 cp = p_fqname(cp, msg, file);
477 putc(' ', file);
478 cp = p_fqname(cp, msg, file);
e515a559
RC
479 break;
480
0d22afd1 481 case T_UINFO:
882ceeb2
MK
482 putc('\t', file);
483 fputs(cp, file);
0d22afd1
RC
484 cp += dlen;
485 break;
486
487 case T_UID:
488 case T_GID:
489 if (dlen == 4) {
882ceeb2
MK
490 fprintf(file,"\t%u", _getlong(cp));
491 cp += sizeof(int32_t);
0d22afd1
RC
492 }
493 break;
494
495 case T_WKS:
882ceeb2 496 if (dlen < sizeof(u_int32_t) + 1)
0d22afd1 497 break;
11e57e73 498 bcopy(cp, (char *)&inaddr, sizeof(inaddr));
882ceeb2
MK
499 cp += sizeof(u_int32_t);
500 fprintf(file, "\t%s %s ( ",
501 inet_ntoa(inaddr),
502 deproto((int) *cp));
503 cp += sizeof(u_char);
0d22afd1 504 n = 0;
882ceeb2 505 lcnt = 0;
0d22afd1
RC
506 while (cp < cp1 + dlen) {
507 c = *cp++;
508 do {
882ceeb2
MK
509 if (c & 0200) {
510 if (lcnt == 0) {
511 fputs("\n\t\t\t", file);
512 lcnt = 5;
513 }
514 fputs(dewks(n), file);
515 putc(' ', file);
516 lcnt--;
517 }
a943feed 518 c <<= 1;
0d22afd1
RC
519 } while (++n & 07);
520 }
882ceeb2 521 putc(')', file);
0d22afd1
RC
522 break;
523
3bf25746 524#ifdef ALLOW_T_UNSPEC
1e12917d
KB
525 case T_UNSPEC:
526 {
527 int NumBytes = 8;
528 char *DataPtr;
529 int i;
3bf25746 530
1e12917d
KB
531 if (dlen < NumBytes) NumBytes = dlen;
532 fprintf(file, "\tFirst %d bytes of hex data:",
533 NumBytes);
534 for (i = 0, DataPtr = cp; i < NumBytes; i++, DataPtr++)
535 fprintf(file, " %x", *DataPtr);
1e12917d
KB
536 cp += dlen;
537 }
538 break;
539#endif /* ALLOW_T_UNSPEC */
3bf25746 540
0d22afd1 541 default:
882ceeb2 542 fprintf(file,"\t?%d?", type);
0d22afd1
RC
543 cp += dlen;
544 }
882ceeb2
MK
545#if 0
546 fprintf(file, "\t; dlen=%d, ttl %s\n", dlen, __p_time(tmpttl));
547#else
548 putc('\n', file);
549#endif
550 if (cp - cp1 != dlen) {
551 fprintf(file,";; packet size error (found %d, dlen was %d)\n",
552 cp - cp1, dlen);
90e07ed6
JB
553 cp = NULL;
554 }
0d22afd1
RC
555 return (cp);
556}
557
7134abf2 558static char nbuf[40];
0d22afd1
RC
559
560/*
561 * Return a string for the type
562 */
c22d8692
KB
563char *
564__p_type(type)
0d22afd1
RC
565 int type;
566{
0d22afd1
RC
567 switch (type) {
568 case T_A:
569 return("A");
570 case T_NS: /* authoritative server */
571 return("NS");
0af159f3 572 case T_CNAME: /* canonical name */
0d22afd1
RC
573 return("CNAME");
574 case T_SOA: /* start of authority zone */
575 return("SOA");
576 case T_MB: /* mailbox domain name */
577 return("MB");
578 case T_MG: /* mail group member */
579 return("MG");
580 case T_MR: /* mail rename name */
581 return("MR");
582 case T_NULL: /* null resource record */
583 return("NULL");
584 case T_WKS: /* well known service */
585 return("WKS");
586 case T_PTR: /* domain name pointer */
587 return("PTR");
588 case T_HINFO: /* host information */
589 return("HINFO");
590 case T_MINFO: /* mailbox information */
591 return("MINFO");
606dc84e
JB
592 case T_MX: /* mail routing info */
593 return("MX");
594 case T_TXT: /* text */
595 return("TXT");
882ceeb2
MK
596 case T_RP: /* responsible person */
597 return("RP");
0d22afd1
RC
598 case T_AXFR: /* zone transfer */
599 return("AXFR");
600 case T_MAILB: /* mail box */
601 return("MAILB");
602 case T_MAILA: /* mail address */
603 return("MAILA");
604 case T_ANY: /* matches any type */
605 return("ANY");
606 case T_UINFO:
607 return("UINFO");
608 case T_UID:
609 return("UID");
610 case T_GID:
611 return("GID");
3bf25746 612#ifdef ALLOW_T_UNSPEC
1e12917d
KB
613 case T_UNSPEC:
614 return("UNSPEC");
615#endif /* ALLOW_T_UNSPEC */
0d22afd1 616 default:
06eef7c4
KB
617 (void)sprintf(nbuf, "%d", type);
618 return(nbuf);
0d22afd1
RC
619 }
620}
621
622/*
623 * Return a mnemonic for class
624 */
c22d8692
KB
625char *
626__p_class(class)
0d22afd1
RC
627 int class;
628{
629
630 switch (class) {
631 case C_IN: /* internet class */
632 return("IN");
0af159f3
JB
633 case C_HS: /* hesiod class */
634 return("HS");
0d22afd1
RC
635 case C_ANY: /* matches any class */
636 return("ANY");
637 default:
06eef7c4
KB
638 (void)sprintf(nbuf, "%d", class);
639 return(nbuf);
0d22afd1
RC
640 }
641}
7134abf2 642
882ceeb2
MK
643/*
644 * Return a mnemonic for an option
645 */
646static char *
647p_option(option)
648 u_int32_t option;
649{
650 switch (option) {
651 case RES_INIT: return "init";
652 case RES_DEBUG: return "debug";
653 case RES_AAONLY: return "aaonly";
654 case RES_USEVC: return "usevc";
655 case RES_PRIMARY: return "primry";
656 case RES_IGNTC: return "igntc";
657 case RES_RECURSE: return "recurs";
658 case RES_DEFNAMES: return "defnam";
659 case RES_STAYOPEN: return "styopn";
660 case RES_DNSRCH: return "dnsrch";
661 default: sprintf(nbuf, "?0x%x?", option); return nbuf;
662 }
663}
664
7134abf2
MK
665/*
666 * Return a mnemonic for a time to live
667 */
16dfe61e
KB
668char *
669__p_time(value)
882ceeb2 670 u_int32_t value;
7134abf2 671{
882ceeb2 672 int secs, mins, hours, days;
7134abf2
MK
673 register char *p;
674
00976ca1
JB
675 if (value == 0) {
676 strcpy(nbuf, "0 secs");
677 return(nbuf);
678 }
679
7134abf2
MK
680 secs = value % 60;
681 value /= 60;
682 mins = value % 60;
683 value /= 60;
684 hours = value % 24;
685 value /= 24;
882ceeb2
MK
686 days = value;
687 value = 0;
7134abf2
MK
688
689#define PLURALIZE(x) x, (x == 1) ? "" : "s"
690 p = nbuf;
882ceeb2
MK
691 if (days) {
692 (void)sprintf(p, "%d day%s", PLURALIZE(days));
7134abf2
MK
693 while (*++p);
694 }
695 if (hours) {
882ceeb2 696 if (days)
7134abf2
MK
697 *p++ = ' ';
698 (void)sprintf(p, "%d hour%s", PLURALIZE(hours));
699 while (*++p);
700 }
701 if (mins) {
882ceeb2 702 if (days || hours)
7134abf2
MK
703 *p++ = ' ';
704 (void)sprintf(p, "%d min%s", PLURALIZE(mins));
705 while (*++p);
706 }
882ceeb2
MK
707 if (secs || ! (days || hours || mins)) {
708 if (days || hours || mins)
7134abf2
MK
709 *p++ = ' ';
710 (void)sprintf(p, "%d sec%s", PLURALIZE(secs));
7134abf2
MK
711 }
712 return(nbuf);
713}