initial import
[glibc.git] / resolv / res_debug.c
blob254e1efc392a6aea11bf0eb3c795c5ecf6fd1f21
1 /*
2 * ++Copyright++ 1985, 1990, 1993
3 * -
4 * Copyright (c) 1985, 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 * -
35 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
37 * Permission to use, copy, modify, and distribute this software for any
38 * purpose with or without fee is hereby granted, provided that the above
39 * copyright notice and this permission notice appear in all copies, and that
40 * the name of Digital Equipment Corporation not be used in advertising or
41 * publicity pertaining to distribution of the document or software without
42 * specific, written prior permission.
44 * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
45 * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
46 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
47 * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
48 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
49 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
50 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
51 * SOFTWARE.
52 * -
53 * --Copyright--
56 #if defined(LIBC_SCCS) && !defined(lint)
57 static char sccsid[] = "@(#)res_debug.c 8.1 (Berkeley) 6/4/93";
58 static char rcsid[] = "$Id$";
59 #endif /* LIBC_SCCS and not lint */
61 #include <sys/param.h>
62 #include <netinet/in.h>
63 #include <arpa/inet.h>
64 #include <arpa/nameser.h>
66 #include <stdio.h>
67 #include <resolv.h>
68 #if defined(BSD) && (BSD >= 199103)
69 # include <string.h>
70 #else
71 # include "../conf/portability.h"
72 #endif
74 #if defined(USE_OPTIONS_H)
75 # include "../conf/options.h"
76 #endif
78 const char *_res_opcodes[] = {
79 "QUERY",
80 "IQUERY",
81 "CQUERYM",
82 "CQUERYU", /* experimental */
83 "NOTIFY", /* experimental */
84 "5",
85 "6",
86 "7",
87 "8",
88 "UPDATEA",
89 "UPDATED",
90 "UPDATEDA",
91 "UPDATEM",
92 "UPDATEMA",
93 "ZONEINIT",
94 "ZONEREF",
97 const char *_res_resultcodes[] = {
98 "NOERROR",
99 "FORMERR",
100 "SERVFAIL",
101 "NXDOMAIN",
102 "NOTIMP",
103 "REFUSED",
104 "6",
105 "7",
106 "8",
107 "9",
108 "10",
109 "11",
110 "12",
111 "13",
112 "14",
113 "NOCHANGE",
116 static char retbuf[16];
118 static const char *
119 dewks(wks)
120 int wks;
122 switch (wks) {
123 case 5: return "rje";
124 case 7: return "echo";
125 case 9: return "discard";
126 case 11: return "systat";
127 case 13: return "daytime";
128 case 15: return "netstat";
129 case 17: return "qotd";
130 case 19: return "chargen";
131 case 20: return "ftp-data";
132 case 21: return "ftp";
133 case 23: return "telnet";
134 case 25: return "smtp";
135 case 37: return "time";
136 case 39: return "rlp";
137 case 42: return "name";
138 case 43: return "whois";
139 case 53: return "domain";
140 case 57: return "apts";
141 case 59: return "apfs";
142 case 67: return "bootps";
143 case 68: return "bootpc";
144 case 69: return "tftp";
145 case 77: return "rje";
146 case 79: return "finger";
147 case 87: return "link";
148 case 95: return "supdup";
149 case 100: return "newacct";
150 case 101: return "hostnames";
151 case 102: return "iso-tsap";
152 case 103: return "x400";
153 case 104: return "x400-snd";
154 case 105: return "csnet-ns";
155 case 109: return "pop-2";
156 case 111: return "sunrpc";
157 case 113: return "auth";
158 case 115: return "sftp";
159 case 117: return "uucp-path";
160 case 119: return "nntp";
161 case 121: return "erpc";
162 case 123: return "ntp";
163 case 133: return "statsrv";
164 case 136: return "profile";
165 case 144: return "NeWS";
166 case 161: return "snmp";
167 case 162: return "snmp-trap";
168 case 170: return "print-srv";
169 default: (void) sprintf(retbuf, "%d", wks); return (retbuf);
173 static const char *
174 deproto(protonum)
175 int protonum;
177 switch (protonum) {
178 case 1: return "icmp";
179 case 2: return "igmp";
180 case 3: return "ggp";
181 case 5: return "st";
182 case 6: return "tcp";
183 case 7: return "ucl";
184 case 8: return "egp";
185 case 9: return "igp";
186 case 11: return "nvp-II";
187 case 12: return "pup";
188 case 16: return "chaos";
189 case 17: return "udp";
190 default: (void) sprintf(retbuf, "%d", protonum); return (retbuf);
194 static const u_char *
195 do_rrset(msg, cp, cnt, pflag, file, hs)
196 int cnt, pflag;
197 const u_char *cp, *msg;
198 const char *hs;
199 FILE *file;
201 int n;
202 int sflag;
205 * Print answer records.
207 sflag = (_res.pfcode & pflag);
208 if (n = ntohs(cnt)) {
209 if ((!_res.pfcode) ||
210 ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
211 fprintf(file, hs);
212 while (--n >= 0) {
213 if ((!_res.pfcode) || sflag) {
214 cp = p_rr(cp, msg, file);
215 } else {
216 unsigned int dlen;
217 cp += __dn_skipname(cp, cp + MAXCDNAME);
218 cp += INT16SZ;
219 cp += INT16SZ;
220 cp += INT32SZ;
221 dlen = _getshort((u_char*)cp);
222 cp += INT16SZ;
223 cp += dlen;
225 if ((cp - msg) > PACKETSZ)
226 return (NULL);
228 if ((!_res.pfcode) ||
229 ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
230 putc('\n', file);
232 return (cp);
235 void
236 __p_query(msg)
237 const u_char *msg;
239 __fp_query(msg, stdout);
242 #ifdef ultrix
243 /* ultrix 4.0's packaging has some icky packaging. alias for it here.
244 * there is more junk of this kind over in res_comp.c.
246 void
247 p_query(msg)
248 const u_char *msg;
250 __p_query(msg);
252 #endif
255 * Print the current options.
256 * This is intended to be primarily a debugging routine.
258 void
259 __fp_resstat(statp, file)
260 struct __res_state *statp;
261 FILE *file;
263 register u_long mask;
265 fprintf(file, ";; res options:");
266 if (!statp)
267 statp = &_res;
268 for (mask = 1; mask != 0; mask <<= 1)
269 if (statp->options & mask)
270 fprintf(file, " %s", p_option(mask));
271 putc('\n', file);
275 * Print the contents of a query.
276 * This is intended to be primarily a debugging routine.
278 void
279 __fp_nquery(msg, len, file)
280 const u_char *msg;
281 int len;
282 FILE *file;
284 register const u_char *cp, *endMark;
285 register const HEADER *hp;
286 register int n;
288 #define TruncTest(x) if (x >= endMark) goto trunc
289 #define ErrorTest(x) if (x == NULL) goto error
292 * Print header fields.
294 hp = (HEADER *)msg;
295 cp = msg + HFIXEDSZ;
296 endMark = cp + len;
297 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEADX) || hp->rcode) {
298 fprintf(file, ";; ->>HEADER<<- opcode: %s, status: %s, id: %d",
299 _res_opcodes[hp->opcode],
300 _res_resultcodes[hp->rcode],
301 ntohs(hp->id));
302 putc('\n', file);
304 putc(';', file);
305 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD2)) {
306 fprintf(file, "; flags:");
307 if (hp->qr)
308 fprintf(file, " qr");
309 if (hp->aa)
310 fprintf(file, " aa");
311 if (hp->tc)
312 fprintf(file, " tc");
313 if (hp->rd)
314 fprintf(file, " rd");
315 if (hp->ra)
316 fprintf(file, " ra");
317 if (hp->pr)
318 fprintf(file, " pr");
320 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD1)) {
321 fprintf(file, "; Ques: %d", ntohs(hp->qdcount));
322 fprintf(file, ", Ans: %d", ntohs(hp->ancount));
323 fprintf(file, ", Auth: %d", ntohs(hp->nscount));
324 fprintf(file, ", Addit: %d", ntohs(hp->arcount));
326 if ((!_res.pfcode) || (_res.pfcode &
327 (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1))) {
328 putc('\n',file);
331 * Print question records.
333 if (n = ntohs(hp->qdcount)) {
334 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
335 fprintf(file, ";; QUESTIONS:\n");
336 while (--n >= 0) {
337 fprintf(file, ";;\t");
338 TruncTest(cp);
339 cp = p_cdname(cp, msg, file);
340 ErrorTest(cp);
341 TruncTest(cp);
342 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
343 fprintf(file, ", type = %s",
344 __p_type(_getshort((u_char*)cp)));
345 cp += INT16SZ;
346 TruncTest(cp);
347 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
348 fprintf(file, ", class = %s\n",
349 __p_class(_getshort((u_char*)cp)));
350 cp += INT16SZ;
351 putc('\n', file);
355 * Print authoritative answer records
357 TruncTest(cp);
358 cp = do_rrset(msg, cp, hp->ancount, RES_PRF_ANS, file,
359 ";; ANSWERS:\n");
360 ErrorTest(cp);
363 * print name server records
365 TruncTest(cp);
366 cp = do_rrset(msg, cp, hp->nscount, RES_PRF_AUTH, file,
367 ";; AUTHORITY RECORDS:\n");
368 ErrorTest(cp);
370 TruncTest(cp);
372 * print additional records
374 cp = do_rrset(msg, cp, hp->arcount, RES_PRF_ADD, file,
375 ";; ADDITIONAL RECORDS:\n");
376 ErrorTest(cp);
377 return;
378 trunc:
379 fprintf(file, "\n;; ...truncated\n");
380 return;
381 error:
382 fprintf(file, "\n;; ...malformed\n");
385 void
386 __fp_query(msg, file)
387 const u_char *msg;
388 FILE *file;
390 fp_nquery(msg, PACKETSZ, file);
393 const u_char *
394 __p_cdnname(cp, msg, len, file)
395 const u_char *cp, *msg;
396 int len;
397 FILE *file;
399 char name[MAXDNAME];
400 int n;
402 if ((n = dn_expand(msg, msg + len, cp, name, sizeof name)) < 0)
403 return (NULL);
404 if (name[0] == '\0')
405 putc('.', file);
406 else
407 fputs(name, file);
408 return (cp + n);
411 const u_char *
412 __p_cdname(cp, msg, file)
413 const u_char *cp, *msg;
414 FILE *file;
416 return (p_cdnname(cp, msg, PACKETSZ, file));
419 /* XXX: the rest of these functions need to become length-limited, too. (vix)
422 const u_char *
423 __p_fqname(cp, msg, file)
424 const u_char *cp, *msg;
425 FILE *file;
427 char name[MAXDNAME];
428 int n, len;
430 if ((n = dn_expand(msg, cp + MAXCDNAME, cp, name, sizeof name)) < 0)
431 return (NULL);
432 if (name[0] == '\0') {
433 putc('.', file);
434 } else {
435 fputs(name, file);
436 if (name[strlen(name) - 1] != '.')
437 putc('.', file);
439 return (cp + n);
443 * Print resource record fields in human readable form.
445 const u_char *
446 __p_rr(cp, msg, file)
447 const u_char *cp, *msg;
448 FILE *file;
450 int type, class, dlen, n, c;
451 struct in_addr inaddr;
452 const u_char *cp1, *cp2;
453 u_int32_t tmpttl, t;
454 int lcnt;
456 if ((cp = p_fqname(cp, msg, file)) == NULL)
457 return (NULL); /* compression error */
458 type = _getshort((u_char*)cp);
459 cp += INT16SZ;
460 class = _getshort((u_char*)cp);
461 cp += INT16SZ;
462 tmpttl = _getlong((u_char*)cp);
463 cp += INT32SZ;
464 dlen = _getshort((u_char*)cp);
465 cp += INT16SZ;
466 cp1 = cp;
467 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_TTLID))
468 fprintf(file, "\t%lu", tmpttl);
469 if ((!_res.pfcode) || (_res.pfcode & RES_PRF_CLASS))
470 fprintf(file, "\t%s", __p_class(class));
471 fprintf(file, "\t%s", __p_type(type));
473 * Print type specific data, if appropriate
475 switch (type) {
476 case T_A:
477 switch (class) {
478 case C_IN:
479 case C_HS:
480 bcopy(cp, (char *)&inaddr, INADDRSZ);
481 if (dlen == 4) {
482 fprintf(file, "\t%s", inet_ntoa(inaddr));
483 cp += dlen;
484 } else if (dlen == 7) {
485 char *address;
486 u_char protocol;
487 u_short port;
489 address = inet_ntoa(inaddr);
490 cp += INADDRSZ;
491 protocol = *(u_char*)cp;
492 cp += sizeof(u_char);
493 port = _getshort((u_char*)cp);
494 cp += INT16SZ;
495 fprintf(file, "\t%s\t; proto %d, port %d",
496 address, protocol, port);
498 break;
499 default:
500 cp += dlen;
502 break;
503 case T_CNAME:
504 case T_MB:
505 case T_MG:
506 case T_MR:
507 case T_NS:
508 case T_PTR:
509 putc('\t', file);
510 if ((cp = p_fqname(cp, msg, file)) == NULL)
511 return (NULL);
512 break;
514 case T_HINFO:
515 case T_ISDN:
516 cp2 = cp + dlen;
517 if (n = *cp++) {
518 fprintf(file, "\t%.*s", n, cp);
519 cp += n;
521 if ((cp < cp2) && (n = *cp++)) {
522 fprintf(file, "\t%.*s", n, cp);
523 cp += n;
524 } else if (type == T_HINFO)
525 fprintf(file, "\n;; *** Warning *** OS-type missing");
526 break;
528 case T_SOA:
529 putc('\t', file);
530 if ((cp = p_fqname(cp, msg, file)) == NULL)
531 return (NULL);
532 putc(' ', file);
533 if ((cp = p_fqname(cp, msg, file)) == NULL)
534 return (NULL);
535 fputs(" (\n", file);
536 t = _getlong((u_char*)cp); cp += INT32SZ;
537 fprintf(file, "\t\t\t%lu\t; serial\n", t);
538 t = _getlong((u_char*)cp); cp += INT32SZ;
539 fprintf(file, "\t\t\t%lu\t; refresh (%s)\n", t, __p_time(t));
540 t = _getlong((u_char*)cp); cp += INT32SZ;
541 fprintf(file, "\t\t\t%lu\t; retry (%s)\n", t, __p_time(t));
542 t = _getlong((u_char*)cp); cp += INT32SZ;
543 fprintf(file, "\t\t\t%lu\t; expire (%s)\n", t, __p_time(t));
544 t = _getlong((u_char*)cp); cp += INT32SZ;
545 fprintf(file, "\t\t\t%lu )\t; minimum (%s)", t, __p_time(t));
546 break;
548 case T_MX:
549 case T_AFSDB:
550 case T_RT:
551 fprintf(file, "\t%d ", _getshort((u_char*)cp));
552 cp += INT16SZ;
553 if ((cp = p_fqname(cp, msg, file)) == NULL)
554 return (NULL);
555 break;
557 case T_PX:
558 fprintf(file, "\t%d ", _getshort((u_char*)cp));
559 cp += INT16SZ;
560 if ((cp = p_fqname(cp, msg, file)) == NULL)
561 return (NULL);
562 putc(' ', file);
563 if ((cp = p_fqname(cp, msg, file)) == NULL)
564 return (NULL);
565 break;
567 case T_TXT:
568 case T_X25:
569 (void) fputs("\t\"", file);
570 cp2 = cp1 + dlen;
571 while (cp < cp2) {
572 if (n = (unsigned char) *cp++) {
573 for (c = n; c > 0 && cp < cp2; c--)
574 if ((*cp == '\n') || (*cp == '"')) {
575 (void) putc('\\', file);
576 (void) putc(*cp++, file);
577 } else
578 (void) putc(*cp++, file);
581 putc('"', file);
582 break;
584 case T_NSAP:
585 (void) fprintf(file, "\t%s", inet_nsap_ntoa(dlen, cp, NULL));
586 cp += dlen;
587 break;
589 case T_MINFO:
590 case T_RP:
591 putc('\t', file);
592 if ((cp = p_fqname(cp, msg, file)) == NULL)
593 return (NULL);
594 putc(' ', file);
595 if ((cp = p_fqname(cp, msg, file)) == NULL)
596 return (NULL);
597 break;
599 case T_UINFO:
600 putc('\t', file);
601 fputs((char *)cp, file);
602 cp += dlen;
603 break;
605 case T_UID:
606 case T_GID:
607 if (dlen == 4) {
608 fprintf(file, "\t%u", _getlong((u_char*)cp));
609 cp += INT32SZ;
611 break;
613 case T_WKS:
614 if (dlen < INT32SZ + 1)
615 break;
616 bcopy(cp, (char *)&inaddr, INADDRSZ);
617 cp += INT32SZ;
618 fprintf(file, "\t%s %s ( ",
619 inet_ntoa(inaddr),
620 deproto((int) *cp));
621 cp += sizeof(u_char);
622 n = 0;
623 lcnt = 0;
624 while (cp < cp1 + dlen) {
625 c = *cp++;
626 do {
627 if (c & 0200) {
628 if (lcnt == 0) {
629 fputs("\n\t\t\t", file);
630 lcnt = 5;
632 fputs(dewks(n), file);
633 putc(' ', file);
634 lcnt--;
636 c <<= 1;
637 } while (++n & 07);
639 putc(')', file);
640 break;
642 #ifdef ALLOW_T_UNSPEC
643 case T_UNSPEC:
645 int NumBytes = 8;
646 u_char *DataPtr;
647 int i;
649 if (dlen < NumBytes) NumBytes = dlen;
650 fprintf(file, "\tFirst %d bytes of hex data:",
651 NumBytes);
652 for (i = 0, DataPtr = cp; i < NumBytes; i++, DataPtr++)
653 fprintf(file, " %x", *DataPtr);
654 cp += dlen;
656 break;
657 #endif /* ALLOW_T_UNSPEC */
659 default:
660 fprintf(file, "\t?%d?", type);
661 cp += dlen;
663 #if 0
664 fprintf(file, "\t; dlen=%d, ttl %s\n", dlen, __p_time(tmpttl));
665 #else
666 putc('\n', file);
667 #endif
668 if (cp - cp1 != dlen) {
669 fprintf(file, ";; packet size error (found %d, dlen was %d)\n",
670 cp - cp1, dlen);
671 cp = NULL;
673 return (cp);
676 static char nbuf[40];
679 * Return a string for the type
681 const char *
682 __p_type(type)
683 int type;
685 switch (type) {
686 case T_A: return "A";
687 case T_NS: return "NS";
688 case T_CNAME: return "CNAME";
689 case T_SOA: return "SOA";
690 case T_MB: return "MB";
691 case T_MG: return "MG";
692 case T_MR: return "MR";
693 case T_NULL: return "NULL";
694 case T_WKS: return "WKS";
695 case T_PTR: return "PTR";
696 case T_HINFO: return "HINFO";
697 case T_MINFO: return "MINFO";
698 case T_MX: return "MX";
699 case T_TXT: return "TXT";
700 case T_RP: return "RP";
701 case T_AFSDB: return "AFSDB";
702 case T_X25: return "X25";
703 case T_ISDN: return "ISDN";
704 case T_RT: return "RT";
705 case T_NSAP: return "NSAP";
706 case T_NSAP_PTR: return "NSAP_PTR";
707 case T_SIG: return "SIG";
708 case T_KEY: return "KEY";
709 case T_PX: return "PX";
710 case T_GPOS: return "GPOS";
711 case T_AAAA: return "AAAA";
712 case T_LOC: return "LOC";
713 case T_AXFR: return "AXFR";
714 case T_MAILB: return "MAILB";
715 case T_MAILA: return "MAILA";
716 case T_ANY: return "ANY";
717 case T_UINFO: return "UINFO";
718 case T_UID: return "UID";
719 case T_GID: return "GID";
720 #ifdef ALLOW_T_UNSPEC
721 case T_UNSPEC: return "UNSPEC";
722 #endif /* ALLOW_T_UNSPEC */
723 default: (void)sprintf(nbuf, "%d", type); return (nbuf);
728 * Return a mnemonic for class
730 const char *
731 __p_class(class)
732 int class;
734 switch (class) {
735 case C_IN: return "IN";
736 case C_HS: return "HS";
737 case C_ANY: return "ANY";
738 default: (void)sprintf(nbuf, "%d", class); return (nbuf);
743 * Return a mnemonic for an option
745 const char *
746 __p_option(option)
747 u_long option;
749 switch (option) {
750 case RES_INIT: return "init";
751 case RES_DEBUG: return "debug";
752 case RES_AAONLY: return "aaonly";
753 case RES_USEVC: return "usevc";
754 case RES_PRIMARY: return "primry";
755 case RES_IGNTC: return "igntc";
756 case RES_RECURSE: return "recurs";
757 case RES_DEFNAMES: return "defnam";
758 case RES_STAYOPEN: return "styopn";
759 case RES_DNSRCH: return "dnsrch";
760 case RES_INSECURE1: return "insecure1";
761 case RES_INSECURE2: return "insecure2";
762 default: sprintf(nbuf, "?0x%x?", option); return (nbuf);
767 * Return a mnemonic for a time to live
769 char *
770 __p_time(value)
771 u_int32_t value;
773 int secs, mins, hours, days;
774 register char *p;
776 if (value == 0) {
777 strcpy(nbuf, "0 secs");
778 return (nbuf);
781 secs = value % 60;
782 value /= 60;
783 mins = value % 60;
784 value /= 60;
785 hours = value % 24;
786 value /= 24;
787 days = value;
788 value = 0;
790 #define PLURALIZE(x) x, (x == 1) ? "" : "s"
791 p = nbuf;
792 if (days) {
793 (void)sprintf(p, "%d day%s", PLURALIZE(days));
794 while (*++p);
796 if (hours) {
797 if (days)
798 *p++ = ' ';
799 (void)sprintf(p, "%d hour%s", PLURALIZE(hours));
800 while (*++p);
802 if (mins) {
803 if (days || hours)
804 *p++ = ' ';
805 (void)sprintf(p, "%d min%s", PLURALIZE(mins));
806 while (*++p);
808 if (secs || ! (days || hours || mins)) {
809 if (days || hours || mins)
810 *p++ = ' ';
811 (void)sprintf(p, "%d sec%s", PLURALIZE(secs));
813 return (nbuf);