summaryrefslogtreecommitdiffstats
path: root/lib/libc/net/res_debug.c
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1994-05-27 05:00:24 +0000
committerrgrimes <rgrimes@FreeBSD.org>1994-05-27 05:00:24 +0000
commit8b1c40bc18615379af063dfafe0085ffa594da60 (patch)
tree643fba97374ab4016af248765b105611ab69f6d6 /lib/libc/net/res_debug.c
downloadFreeBSD-src-8b1c40bc18615379af063dfafe0085ffa594da60.zip
FreeBSD-src-8b1c40bc18615379af063dfafe0085ffa594da60.tar.gz
BSD 4.4 Lite Lib Sources
Diffstat (limited to 'lib/libc/net/res_debug.c')
-rw-r--r--lib/libc/net/res_debug.c739
1 files changed, 739 insertions, 0 deletions
diff --git a/lib/libc/net/res_debug.c b/lib/libc/net/res_debug.c
new file mode 100644
index 0000000..0bd882a
--- /dev/null
+++ b/lib/libc/net/res_debug.c
@@ -0,0 +1,739 @@
+/*-
+ * Copyright (c) 1985, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)res_debug.c 8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <string.h>
+
+void __fp_query();
+char *__p_class(), *__p_time(), *__p_type();
+char *p_cdname(), *p_fqname(), *p_rr();
+static char *p_option __P((u_int32_t));
+
+char *_res_opcodes[] = {
+ "QUERY",
+ "IQUERY",
+ "CQUERYM",
+ "CQUERYU",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "UPDATEA",
+ "UPDATED",
+ "UPDATEDA",
+ "UPDATEM",
+ "UPDATEMA",
+ "ZONEINIT",
+ "ZONEREF",
+};
+
+char *_res_resultcodes[] = {
+ "NOERROR",
+ "FORMERR",
+ "SERVFAIL",
+ "NXDOMAIN",
+ "NOTIMP",
+ "REFUSED",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "NOCHANGE",
+};
+
+static char retbuf[16];
+
+static char *
+dewks(wks)
+ int wks;
+{
+ switch (wks) {
+ case 5: return("rje");
+ case 7: return("echo");
+ case 9: return("discard");
+ case 11: return("systat");
+ case 13: return("daytime");
+ case 15: return("netstat");
+ case 17: return("qotd");
+ case 19: return("chargen");
+ case 20: return("ftp-data");
+ case 21: return("ftp");
+ case 23: return("telnet");
+ case 25: return("smtp");
+ case 37: return("time");
+ case 39: return("rlp");
+ case 42: return("name");
+ case 43: return("whois");
+ case 53: return("domain");
+ case 57: return("apts");
+ case 59: return("apfs");
+ case 67: return("bootps");
+ case 68: return("bootpc");
+ case 69: return("tftp");
+ case 77: return("rje");
+ case 79: return("finger");
+ case 87: return("link");
+ case 95: return("supdup");
+ case 100: return("newacct");
+ case 101: return("hostnames");
+ case 102: return("iso-tsap");
+ case 103: return("x400");
+ case 104: return("x400-snd");
+ case 105: return("csnet-ns");
+ case 109: return("pop-2");
+ case 111: return("sunrpc");
+ case 113: return("auth");
+ case 115: return("sftp");
+ case 117: return("uucp-path");
+ case 119: return("nntp");
+ case 121: return("erpc");
+ case 123: return("ntp");
+ case 133: return("statsrv");
+ case 136: return("profile");
+ case 144: return("NeWS");
+ case 161: return("snmp");
+ case 162: return("snmp-trap");
+ case 170: return("print-srv");
+ default: (void) sprintf(retbuf, "%d", wks); return(retbuf);
+ }
+}
+
+static char *
+deproto(protonum)
+ int protonum;
+{
+ switch (protonum) {
+ case 1: return("icmp");
+ case 2: return("igmp");
+ case 3: return("ggp");
+ case 5: return("st");
+ case 6: return("tcp");
+ case 7: return("ucl");
+ case 8: return("egp");
+ case 9: return("igp");
+ case 11: return("nvp-II");
+ case 12: return("pup");
+ case 16: return("chaos");
+ case 17: return("udp");
+ default: (void) sprintf(retbuf, "%d", protonum); return(retbuf);
+ }
+}
+
+static char *
+do_rrset(msg, cp, cnt, pflag, file, hs)
+ int cnt, pflag;
+ char *cp,*msg, *hs;
+ FILE *file;
+{
+ int n;
+ int sflag;
+ /*
+ * Print answer records
+ */
+ sflag = (_res.pfcode & pflag);
+ if (n = ntohs(cnt)) {
+ if ((!_res.pfcode) || ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
+ fprintf(file, hs);
+ while (--n >= 0) {
+ cp = p_rr(cp, msg, file);
+ if ((cp-msg) > PACKETSZ)
+ return (NULL);
+ }
+ if ((!_res.pfcode) || ((sflag) && (_res.pfcode & RES_PRF_HEAD1)))
+ putc('\n', file);
+ }
+ return(cp);
+}
+
+__p_query(msg)
+ char *msg;
+{
+ __fp_query(msg, stdout);
+}
+
+/*
+ * Print the current options.
+ * This is intended to be primarily a debugging routine.
+ */
+void
+__fp_resstat(statp, file)
+ struct __res_state *statp;
+ FILE *file;
+{
+ int bit;
+
+ fprintf(file, ";; res options:");
+ if (!statp)
+ statp = &_res;
+ for (bit = 0; bit < 32; bit++) { /* XXX 32 - bad assumption! */
+ if (statp->options & (1<<bit))
+ fprintf(file, " %s", p_option(1<<bit));
+ }
+ putc('\n', file);
+}
+
+/*
+ * Print the contents of a query.
+ * This is intended to be primarily a debugging routine.
+ */
+void
+__fp_query(msg,file)
+ char *msg;
+ FILE *file;
+{
+ register char *cp;
+ register HEADER *hp;
+ register int n;
+
+ /*
+ * Print header fields.
+ */
+ hp = (HEADER *)msg;
+ cp = msg + sizeof(HEADER);
+ if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEADX) || hp->rcode) {
+ fprintf(file,";; ->>HEADER<<- opcode: %s, status: %s, id: %d",
+ _res_opcodes[hp->opcode],
+ _res_resultcodes[hp->rcode],
+ ntohs(hp->id));
+ putc('\n', file);
+ }
+ putc(';', file);
+ if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD2)) {
+ fprintf(file,"; flags:");
+ if (hp->qr)
+ fprintf(file," qr");
+ if (hp->aa)
+ fprintf(file," aa");
+ if (hp->tc)
+ fprintf(file," tc");
+ if (hp->rd)
+ fprintf(file," rd");
+ if (hp->ra)
+ fprintf(file," ra");
+ if (hp->pr)
+ fprintf(file," pr");
+ }
+ if ((!_res.pfcode) || (_res.pfcode & RES_PRF_HEAD1)) {
+ fprintf(file,"; Ques: %d", ntohs(hp->qdcount));
+ fprintf(file,", Ans: %d", ntohs(hp->ancount));
+ fprintf(file,", Auth: %d", ntohs(hp->nscount));
+ fprintf(file,", Addit: %d\n", ntohs(hp->arcount));
+ }
+#if 0
+ if (_res.pfcode & (RES_PRF_HEADX | RES_PRF_HEAD2 | RES_PRF_HEAD1)) {
+ putc('\n',file);
+ }
+#endif
+ /*
+ * Print question records.
+ */
+ if (n = ntohs(hp->qdcount)) {
+ if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
+ fprintf(file,";; QUESTIONS:\n");
+ while (--n >= 0) {
+ fprintf(file,";;\t");
+ cp = p_cdname(cp, msg, file);
+ if (cp == NULL)
+ return;
+ if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
+ fprintf(file, ", type = %s",
+ __p_type(_getshort(cp)));
+ cp += sizeof(u_int16_t);
+ if ((!_res.pfcode) || (_res.pfcode & RES_PRF_QUES))
+ fprintf(file, ", class = %s\n\n",
+ __p_class(_getshort(cp)));
+ cp += sizeof(u_int16_t);
+ }
+ }
+ /*
+ * Print authoritative answer records
+ */
+ cp = do_rrset(msg, cp, hp->ancount, RES_PRF_ANS, file,
+ ";; ANSWERS:\n");
+ if (cp == NULL)
+ return;
+
+ /*
+ * print name server records
+ */
+ cp = do_rrset(msg, cp, hp->nscount, RES_PRF_AUTH, file,
+ ";; AUTHORITY RECORDS:\n");
+ if (!cp)
+ return;
+
+ /*
+ * print additional records
+ */
+ cp = do_rrset(msg, cp, hp->arcount, RES_PRF_ADD, file,
+ ";; ADDITIONAL RECORDS:\n");
+ if (!cp)
+ return;
+}
+
+char *
+p_cdname(cp, msg, file)
+ char *cp, *msg;
+ FILE *file;
+{
+ char name[MAXDNAME];
+ int n;
+
+ if ((n = dn_expand((u_char *)msg, (u_char *)msg + MAXCDNAME,
+ (u_char *)cp, (u_char *)name, sizeof(name))) < 0)
+ return (NULL);
+ if (name[0] == '\0')
+ putc('.', file);
+ else
+ fputs(name, file);
+ return (cp + n);
+}
+
+char *
+p_fqname(cp, msg, file)
+ char *cp, *msg;
+ FILE *file;
+{
+ char name[MAXDNAME];
+ int n, len;
+
+ if ((n = dn_expand((u_char *)msg, (u_char *)msg + MAXCDNAME,
+ (u_char *)cp, (u_char *)name, sizeof(name))) < 0)
+ return (NULL);
+ if (name[0] == '\0') {
+ putc('.', file);
+ } else {
+ fputs(name, file);
+ if (name[strlen(name) - 1] != '.')
+ putc('.', file);
+ }
+ return (cp + n);
+}
+
+/*
+ * Print resource record fields in human readable form.
+ */
+char *
+p_rr(cp, msg, file)
+ char *cp, *msg;
+ FILE *file;
+{
+ int type, class, dlen, n, c;
+ struct in_addr inaddr;
+ char *cp1, *cp2;
+ u_int32_t tmpttl, t;
+ int lcnt;
+
+ if ((cp = p_fqname(cp, msg, file)) == NULL)
+ return (NULL); /* compression error */
+ type = _getshort(cp);
+ cp += sizeof(u_int16_t);
+ class = _getshort(cp);
+ cp += sizeof(u_int16_t);
+ tmpttl = _getlong(cp);
+ cp += sizeof(u_int32_t);
+ dlen = _getshort(cp);
+ cp += sizeof(u_int16_t);
+ cp1 = cp;
+ if ((!_res.pfcode) || (_res.pfcode & RES_PRF_TTLID))
+ fprintf(file, "\t%lu", tmpttl);
+ if ((!_res.pfcode) || (_res.pfcode & RES_PRF_CLASS))
+ fprintf(file, "\t%s", __p_class(class));
+ fprintf(file, "\t%s", __p_type(type));
+ /*
+ * Print type specific data, if appropriate
+ */
+ switch (type) {
+ case T_A:
+ switch (class) {
+ case C_IN:
+ case C_HS:
+ bcopy(cp, (char *)&inaddr, sizeof(inaddr));
+ if (dlen == 4) {
+ fprintf(file,"\t%s", inet_ntoa(inaddr));
+ cp += dlen;
+ } else if (dlen == 7) {
+ char *address;
+ u_char protocol;
+ u_short port;
+
+ address = inet_ntoa(inaddr);
+ cp += sizeof(inaddr);
+ protocol = *(u_char*)cp;
+ cp += sizeof(u_char);
+ port = _getshort(cp);
+ cp += sizeof(u_int16_t);
+ fprintf(file, "\t%s\t; proto %d, port %d",
+ address, protocol, port);
+ }
+ break;
+ default:
+ cp += dlen;
+ }
+ break;
+ case T_CNAME:
+ case T_MB:
+ case T_MG:
+ case T_MR:
+ case T_NS:
+ case T_PTR:
+ putc('\t', file);
+ cp = p_fqname(cp, msg, file);
+ break;
+
+ case T_HINFO:
+ if (n = *cp++) {
+ fprintf(file,"\t%.*s", n, cp);
+ cp += n;
+ }
+ if (n = *cp++) {
+ fprintf(file,"\t%.*s", n, cp);
+ cp += n;
+ }
+ break;
+
+ case T_SOA:
+ putc('\t', file);
+ cp = p_fqname(cp, msg, file); /* origin */
+ putc(' ', file);
+ cp = p_fqname(cp, msg, file); /* mail addr */
+ fputs(" (\n", file);
+ t = _getlong(cp); cp += sizeof(u_int32_t);
+ fprintf(file,"\t\t\t%lu\t; serial\n", t);
+ t = _getlong(cp); cp += sizeof(u_int32_t);
+ fprintf(file,"\t\t\t%lu\t; refresh (%s)\n", t, __p_time(t));
+ t = _getlong(cp); cp += sizeof(u_int32_t);
+ fprintf(file,"\t\t\t%lu\t; retry (%s)\n", t, __p_time(t));
+ t = _getlong(cp); cp += sizeof(u_int32_t);
+ fprintf(file,"\t\t\t%lu\t; expire (%s)\n", t, __p_time(t));
+ t = _getlong(cp); cp += sizeof(u_int32_t);
+ fprintf(file,"\t\t\t%lu )\t; minimum (%s)", t, __p_time(t));
+ break;
+
+ case T_MX:
+ fprintf(file,"\t%d ", _getshort(cp));
+ cp += sizeof(u_int16_t);
+ cp = p_fqname(cp, msg, file);
+ break;
+
+ case T_TXT:
+ (void) fputs("\t\"", file);
+ cp2 = cp1 + dlen;
+ while (cp < cp2) {
+ if (n = (unsigned char) *cp++) {
+ for (c = n; c > 0 && cp < cp2; c--)
+ if (*cp == '\n') {
+ (void) putc('\\', file);
+ (void) putc(*cp++, file);
+ } else
+ (void) putc(*cp++, file);
+ }
+ }
+ putc('"', file);
+ break;
+
+ case T_MINFO:
+ case T_RP:
+ putc('\t', file);
+ cp = p_fqname(cp, msg, file);
+ putc(' ', file);
+ cp = p_fqname(cp, msg, file);
+ break;
+
+ case T_UINFO:
+ putc('\t', file);
+ fputs(cp, file);
+ cp += dlen;
+ break;
+
+ case T_UID:
+ case T_GID:
+ if (dlen == 4) {
+ fprintf(file,"\t%u", _getlong(cp));
+ cp += sizeof(int32_t);
+ }
+ break;
+
+ case T_WKS:
+ if (dlen < sizeof(u_int32_t) + 1)
+ break;
+ bcopy(cp, (char *)&inaddr, sizeof(inaddr));
+ cp += sizeof(u_int32_t);
+ fprintf(file, "\t%s %s ( ",
+ inet_ntoa(inaddr),
+ deproto((int) *cp));
+ cp += sizeof(u_char);
+ n = 0;
+ lcnt = 0;
+ while (cp < cp1 + dlen) {
+ c = *cp++;
+ do {
+ if (c & 0200) {
+ if (lcnt == 0) {
+ fputs("\n\t\t\t", file);
+ lcnt = 5;
+ }
+ fputs(dewks(n), file);
+ putc(' ', file);
+ lcnt--;
+ }
+ c <<= 1;
+ } while (++n & 07);
+ }
+ putc(')', file);
+ break;
+
+#ifdef ALLOW_T_UNSPEC
+ case T_UNSPEC:
+ {
+ int NumBytes = 8;
+ char *DataPtr;
+ int i;
+
+ if (dlen < NumBytes) NumBytes = dlen;
+ fprintf(file, "\tFirst %d bytes of hex data:",
+ NumBytes);
+ for (i = 0, DataPtr = cp; i < NumBytes; i++, DataPtr++)
+ fprintf(file, " %x", *DataPtr);
+ cp += dlen;
+ }
+ break;
+#endif /* ALLOW_T_UNSPEC */
+
+ default:
+ fprintf(file,"\t?%d?", type);
+ cp += dlen;
+ }
+#if 0
+ fprintf(file, "\t; dlen=%d, ttl %s\n", dlen, __p_time(tmpttl));
+#else
+ putc('\n', file);
+#endif
+ if (cp - cp1 != dlen) {
+ fprintf(file,";; packet size error (found %d, dlen was %d)\n",
+ cp - cp1, dlen);
+ cp = NULL;
+ }
+ return (cp);
+}
+
+static char nbuf[40];
+
+/*
+ * Return a string for the type
+ */
+char *
+__p_type(type)
+ int type;
+{
+ switch (type) {
+ case T_A:
+ return("A");
+ case T_NS: /* authoritative server */
+ return("NS");
+ case T_CNAME: /* canonical name */
+ return("CNAME");
+ case T_SOA: /* start of authority zone */
+ return("SOA");
+ case T_MB: /* mailbox domain name */
+ return("MB");
+ case T_MG: /* mail group member */
+ return("MG");
+ case T_MR: /* mail rename name */
+ return("MR");
+ case T_NULL: /* null resource record */
+ return("NULL");
+ case T_WKS: /* well known service */
+ return("WKS");
+ case T_PTR: /* domain name pointer */
+ return("PTR");
+ case T_HINFO: /* host information */
+ return("HINFO");
+ case T_MINFO: /* mailbox information */
+ return("MINFO");
+ case T_MX: /* mail routing info */
+ return("MX");
+ case T_TXT: /* text */
+ return("TXT");
+ case T_RP: /* responsible person */
+ return("RP");
+ case T_AXFR: /* zone transfer */
+ return("AXFR");
+ case T_MAILB: /* mail box */
+ return("MAILB");
+ case T_MAILA: /* mail address */
+ return("MAILA");
+ case T_ANY: /* matches any type */
+ return("ANY");
+ case T_UINFO:
+ return("UINFO");
+ case T_UID:
+ return("UID");
+ case T_GID:
+ return("GID");
+#ifdef ALLOW_T_UNSPEC
+ case T_UNSPEC:
+ return("UNSPEC");
+#endif /* ALLOW_T_UNSPEC */
+ default:
+ (void)sprintf(nbuf, "%d", type);
+ return(nbuf);
+ }
+}
+
+/*
+ * Return a mnemonic for class
+ */
+char *
+__p_class(class)
+ int class;
+{
+
+ switch (class) {
+ case C_IN: /* internet class */
+ return("IN");
+ case C_HS: /* hesiod class */
+ return("HS");
+ case C_ANY: /* matches any class */
+ return("ANY");
+ default:
+ (void)sprintf(nbuf, "%d", class);
+ return(nbuf);
+ }
+}
+
+/*
+ * Return a mnemonic for an option
+ */
+static char *
+p_option(option)
+ u_int32_t option;
+{
+ switch (option) {
+ case RES_INIT: return "init";
+ case RES_DEBUG: return "debug";
+ case RES_AAONLY: return "aaonly";
+ case RES_USEVC: return "usevc";
+ case RES_PRIMARY: return "primry";
+ case RES_IGNTC: return "igntc";
+ case RES_RECURSE: return "recurs";
+ case RES_DEFNAMES: return "defnam";
+ case RES_STAYOPEN: return "styopn";
+ case RES_DNSRCH: return "dnsrch";
+ default: sprintf(nbuf, "?0x%x?", option); return nbuf;
+ }
+}
+
+/*
+ * Return a mnemonic for a time to live
+ */
+char *
+__p_time(value)
+ u_int32_t value;
+{
+ int secs, mins, hours, days;
+ register char *p;
+
+ if (value == 0) {
+ strcpy(nbuf, "0 secs");
+ return(nbuf);
+ }
+
+ secs = value % 60;
+ value /= 60;
+ mins = value % 60;
+ value /= 60;
+ hours = value % 24;
+ value /= 24;
+ days = value;
+ value = 0;
+
+#define PLURALIZE(x) x, (x == 1) ? "" : "s"
+ p = nbuf;
+ if (days) {
+ (void)sprintf(p, "%d day%s", PLURALIZE(days));
+ while (*++p);
+ }
+ if (hours) {
+ if (days)
+ *p++ = ' ';
+ (void)sprintf(p, "%d hour%s", PLURALIZE(hours));
+ while (*++p);
+ }
+ if (mins) {
+ if (days || hours)
+ *p++ = ' ';
+ (void)sprintf(p, "%d min%s", PLURALIZE(mins));
+ while (*++p);
+ }
+ if (secs || ! (days || hours || mins)) {
+ if (days || hours || mins)
+ *p++ = ' ';
+ (void)sprintf(p, "%d sec%s", PLURALIZE(secs));
+ }
+ return(nbuf);
+}
OpenPOWER on IntegriCloud