Update.
[glibc.git] / resolv / ns_print.c
blobcce3fb611d20ab0c6c55536634989384a4538a27
1 /*
2 * Copyright (c) 1996-1999 by Internet Software Consortium.
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
9 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
10 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
11 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
13 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
14 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
15 * SOFTWARE.
18 #ifndef lint
19 static const char rcsid[] = "$Id$";
20 #endif
22 /* Import. */
23 #include <sys/types.h>
24 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <arpa/nameser.h>
28 #include <arpa/inet.h>
30 #include <errno.h>
31 #include <resolv.h>
32 #include <string.h>
33 #include <ctype.h>
35 #ifdef SPRINTF_CHAR
36 # define SPRINTF(x) strlen(sprintf/**/x)
37 #else
38 # define SPRINTF(x) ((size_t)sprintf x)
39 #endif
41 /* Forward. */
43 static size_t prune_origin(const char *name, const char *origin);
44 static int charstr(const u_char *rdata, const u_char *edata,
45 char **buf, size_t *buflen);
46 static int addname(const u_char *msg, size_t msglen,
47 const u_char **p, const char *origin,
48 char **buf, size_t *buflen);
49 static void addlen(size_t len, char **buf, size_t *buflen);
50 static int addstr(const char *src, size_t len,
51 char **buf, size_t *buflen);
52 static int addtab(size_t len, size_t target, int spaced,
53 char **buf, size_t *buflen);
55 /* Proto. */
56 #ifndef _LIBC
57 u_int16_t dst_s_dns_key_id(const u_char *, const int);
58 #endif
60 /* Macros. */
62 #define T(x) \
63 do { \
64 if ((x) < 0) \
65 return (-1); \
66 } while (0)
68 /* Public. */
71 * int
72 * ns_sprintrr(handle, rr, name_ctx, origin, buf, buflen)
73 * Convert an RR to presentation format.
74 * return:
75 * Number of characters written to buf, or -1 (check errno).
77 int
78 ns_sprintrr(const ns_msg *handle, const ns_rr *rr,
79 const char *name_ctx, const char *origin,
80 char *buf, size_t buflen)
82 int n;
84 n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle),
85 ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr),
86 ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr),
87 name_ctx, origin, buf, buflen);
88 return (n);
92 * int
93 * ns_sprintrrf(msg, msglen, name, class, type, ttl, rdata, rdlen,
94 * name_ctx, origin, buf, buflen)
95 * Convert the fields of an RR into presentation format.
96 * return:
97 * Number of characters written to buf, or -1 (check errno).
99 int
100 ns_sprintrrf(const u_char *msg, size_t msglen,
101 const char *name, ns_class class, ns_type type,
102 u_long ttl, const u_char *rdata, size_t rdlen,
103 const char *name_ctx, const char *origin,
104 char *buf, size_t buflen)
106 const char *obuf = buf;
107 const u_char *edata = rdata + rdlen;
108 int spaced = 0;
110 const char *comment;
111 char tmp[100];
112 int len, x;
115 * Owner.
117 if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) {
118 T(addstr("\t\t\t", 3, &buf, &buflen));
119 } else {
120 len = prune_origin(name, origin);
121 if (len == 0) {
122 T(addstr("@\t\t\t", 4, &buf, &buflen));
123 } else {
124 T(addstr(name, len, &buf, &buflen));
125 /* Origin not used and no trailing dot? */
126 if ((!origin || !origin[0] || name[len] == '\0') &&
127 name[len - 1] != '.') {
128 T(addstr(".", 1, &buf, &buflen));
129 len++;
131 T(spaced = addtab(len, 24, spaced, &buf, &buflen));
136 * TTL, Class, Type.
138 T(x = ns_format_ttl(ttl, buf, buflen));
139 addlen(x, &buf, &buflen);
140 len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type)));
141 T(addstr(tmp, len, &buf, &buflen));
142 T(spaced = addtab(x + len, 16, spaced, &buf, &buflen));
145 * RData.
147 switch (type) {
148 case ns_t_a:
149 if (rdlen != NS_INADDRSZ)
150 goto formerr;
151 (void) inet_ntop(AF_INET, rdata, buf, buflen);
152 addlen(strlen(buf), &buf, &buflen);
153 break;
155 case ns_t_cname:
156 case ns_t_mb:
157 case ns_t_mg:
158 case ns_t_mr:
159 case ns_t_ns:
160 case ns_t_ptr:
161 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
162 break;
164 case ns_t_hinfo:
165 case ns_t_isdn:
166 /* First word. */
167 T(len = charstr(rdata, edata, &buf, &buflen));
168 if (len == 0)
169 goto formerr;
170 rdata += len;
171 T(addstr(" ", 1, &buf, &buflen));
174 /* Second word, optional in ISDN records. */
175 if (type == ns_t_isdn && rdata == edata)
176 break;
178 T(len = charstr(rdata, edata, &buf, &buflen));
179 if (len == 0)
180 goto formerr;
181 rdata += len;
182 break;
184 case ns_t_soa: {
185 u_long t;
187 /* Server name. */
188 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
189 T(addstr(" ", 1, &buf, &buflen));
191 /* Administrator name. */
192 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
193 T(addstr(" (\n", 3, &buf, &buflen));
194 spaced = 0;
196 if ((edata - rdata) != 5*NS_INT32SZ)
197 goto formerr;
199 /* Serial number. */
200 t = ns_get32(rdata); rdata += NS_INT32SZ;
201 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
202 len = SPRINTF((tmp, "%lu", t));
203 T(addstr(tmp, len, &buf, &buflen));
204 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
205 T(addstr("; serial\n", 9, &buf, &buflen));
206 spaced = 0;
208 /* Refresh interval. */
209 t = ns_get32(rdata); rdata += NS_INT32SZ;
210 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
211 T(len = ns_format_ttl(t, buf, buflen));
212 addlen(len, &buf, &buflen);
213 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
214 T(addstr("; refresh\n", 10, &buf, &buflen));
215 spaced = 0;
217 /* Retry interval. */
218 t = ns_get32(rdata); rdata += NS_INT32SZ;
219 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
220 T(len = ns_format_ttl(t, buf, buflen));
221 addlen(len, &buf, &buflen);
222 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
223 T(addstr("; retry\n", 8, &buf, &buflen));
224 spaced = 0;
226 /* Expiry. */
227 t = ns_get32(rdata); rdata += NS_INT32SZ;
228 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
229 T(len = ns_format_ttl(t, buf, buflen));
230 addlen(len, &buf, &buflen);
231 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
232 T(addstr("; expiry\n", 9, &buf, &buflen));
233 spaced = 0;
235 /* Minimum TTL. */
236 t = ns_get32(rdata); rdata += NS_INT32SZ;
237 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
238 T(len = ns_format_ttl(t, buf, buflen));
239 addlen(len, &buf, &buflen);
240 T(addstr(" )", 2, &buf, &buflen));
241 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
242 T(addstr("; minimum\n", 10, &buf, &buflen));
244 break;
247 case ns_t_mx:
248 case ns_t_afsdb:
249 case ns_t_rt: {
250 u_int t;
252 if (rdlen < NS_INT16SZ)
253 goto formerr;
255 /* Priority. */
256 t = ns_get16(rdata);
257 rdata += NS_INT16SZ;
258 len = SPRINTF((tmp, "%u ", t));
259 T(addstr(tmp, len, &buf, &buflen));
261 /* Target. */
262 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
264 break;
267 case ns_t_px: {
268 u_int t;
270 if (rdlen < NS_INT16SZ)
271 goto formerr;
273 /* Priority. */
274 t = ns_get16(rdata);
275 rdata += NS_INT16SZ;
276 len = SPRINTF((tmp, "%u ", t));
277 T(addstr(tmp, len, &buf, &buflen));
279 /* Name1. */
280 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
281 T(addstr(" ", 1, &buf, &buflen));
283 /* Name2. */
284 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
286 break;
289 case ns_t_x25:
290 T(len = charstr(rdata, edata, &buf, &buflen));
291 if (len == 0)
292 goto formerr;
293 rdata += len;
294 break;
296 case ns_t_txt:
297 while (rdata < edata) {
298 T(len = charstr(rdata, edata, &buf, &buflen));
299 if (len == 0)
300 goto formerr;
301 rdata += len;
302 if (rdata < edata)
303 T(addstr(" ", 1, &buf, &buflen));
305 break;
307 case ns_t_nsap: {
308 char t[255*3];
310 (void) inet_nsap_ntoa(rdlen, rdata, t);
311 T(addstr(t, strlen(t), &buf, &buflen));
312 break;
315 case ns_t_aaaa:
316 if (rdlen != NS_IN6ADDRSZ)
317 goto formerr;
318 (void) inet_ntop(AF_INET6, rdata, buf, buflen);
319 addlen(strlen(buf), &buf, &buflen);
320 break;
322 case ns_t_loc: {
323 char t[255];
325 /* XXX protocol format checking? */
326 (void) loc_ntoa(rdata, t);
327 T(addstr(t, strlen(t), &buf, &buflen));
328 break;
331 case ns_t_naptr: {
332 u_int order, preference;
333 char t[50];
335 if (rdlen < 2*NS_INT16SZ)
336 goto formerr;
338 /* Order, Precedence. */
339 order = ns_get16(rdata); rdata += NS_INT16SZ;
340 preference = ns_get16(rdata); rdata += NS_INT16SZ;
341 len = SPRINTF((t, "%u %u ", order, preference));
342 T(addstr(t, len, &buf, &buflen));
344 /* Flags. */
345 T(len = charstr(rdata, edata, &buf, &buflen));
346 if (len == 0)
347 goto formerr;
348 rdata += len;
349 T(addstr(" ", 1, &buf, &buflen));
351 /* Service. */
352 T(len = charstr(rdata, edata, &buf, &buflen));
353 if (len == 0)
354 goto formerr;
355 rdata += len;
356 T(addstr(" ", 1, &buf, &buflen));
358 /* Regexp. */
359 T(len = charstr(rdata, edata, &buf, &buflen));
360 if (len < 0)
361 return (-1);
362 if (len == 0)
363 goto formerr;
364 rdata += len;
365 T(addstr(" ", 1, &buf, &buflen));
367 /* Server. */
368 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
369 break;
372 case ns_t_srv: {
373 u_int priority, weight, port;
374 char t[50];
376 if (rdlen < NS_INT16SZ*3)
377 goto formerr;
379 /* Priority, Weight, Port. */
380 priority = ns_get16(rdata); rdata += NS_INT16SZ;
381 weight = ns_get16(rdata); rdata += NS_INT16SZ;
382 port = ns_get16(rdata); rdata += NS_INT16SZ;
383 len = SPRINTF((t, "%u %u %u ", priority, weight, port));
384 T(addstr(t, len, &buf, &buflen));
386 /* Server. */
387 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
388 break;
391 case ns_t_minfo:
392 case ns_t_rp:
393 /* Name1. */
394 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
395 T(addstr(" ", 1, &buf, &buflen));
397 /* Name2. */
398 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
400 break;
402 case ns_t_wks: {
403 int n, lcnt;
405 if (rdlen < NS_INT32SZ + 1)
406 goto formerr;
408 /* Address. */
409 (void) inet_ntop(AF_INET, rdata, buf, buflen);
410 addlen(strlen(buf), &buf, &buflen);
411 rdata += NS_INADDRSZ;
413 /* Protocol. */
414 len = SPRINTF((tmp, " %u ( ", *rdata));
415 T(addstr(tmp, len, &buf, &buflen));
416 rdata += NS_INT8SZ;
418 /* Bit map. */
419 n = 0;
420 lcnt = 0;
421 while (rdata < edata) {
422 u_int c = *rdata++;
423 do {
424 if (c & 0200) {
425 if (lcnt == 0) {
426 T(addstr("\n\t\t\t\t", 5,
427 &buf, &buflen));
428 lcnt = 10;
429 spaced = 0;
431 len = SPRINTF((tmp, "%d ", n));
432 T(addstr(tmp, len, &buf, &buflen));
433 lcnt--;
435 c <<= 1;
436 } while (++n & 07);
438 T(addstr(")", 1, &buf, &buflen));
440 break;
443 case ns_t_key: {
444 #ifndef _LIBC
445 char base64_key[NS_MD5RSA_MAX_BASE64];
446 u_int keyflags, protocol, algorithm, key_id;
447 const char *leader;
448 int n;
450 if (rdlen < NS_INT16SZ + NS_INT8SZ + NS_INT8SZ)
451 goto formerr;
453 /* Key flags, Protocol, Algorithm. */
454 key_id = dst_s_dns_key_id(rdata, edata-rdata);
455 keyflags = ns_get16(rdata); rdata += NS_INT16SZ;
456 protocol = *rdata++;
457 algorithm = *rdata++;
458 len = SPRINTF((tmp, "0x%04x %u %u",
459 keyflags, protocol, algorithm));
460 T(addstr(tmp, len, &buf, &buflen));
462 /* Public key data. */
463 len = b64_ntop(rdata, edata - rdata,
464 base64_key, sizeof base64_key);
465 if (len < 0)
466 goto formerr;
467 if (len > 15) {
468 T(addstr(" (", 2, &buf, &buflen));
469 leader = "\n\t\t";
470 spaced = 0;
471 } else
472 leader = " ";
473 for (n = 0; n < len; n += 48) {
474 T(addstr(leader, strlen(leader), &buf, &buflen));
475 T(addstr(base64_key + n, MIN(len - n, 48),
476 &buf, &buflen));
478 if (len > 15)
479 T(addstr(" )", 2, &buf, &buflen));
480 n = SPRINTF((tmp, " ; key_tag= %u", key_id));
481 T(addstr(tmp, n, &buf, &buflen));
482 #endif /* !_LIBC */
483 break;
486 case ns_t_sig: {
487 #ifndef _LIBC
488 char base64_key[NS_MD5RSA_MAX_BASE64];
489 u_int type, algorithm, labels, footprint;
490 const char *leader;
491 u_long t;
492 int n;
494 if (rdlen < 22)
495 goto formerr;
497 /* Type covered, Algorithm, Label count, Original TTL. */
498 type = ns_get16(rdata); rdata += NS_INT16SZ;
499 algorithm = *rdata++;
500 labels = *rdata++;
501 t = ns_get32(rdata); rdata += NS_INT32SZ;
502 len = SPRINTF((tmp, "%s %d %d %lu ",
503 p_type(type), algorithm, labels, t));
504 T(addstr(tmp, len, &buf, &buflen));
505 if (labels > (u_int)dn_count_labels(name))
506 goto formerr;
508 /* Signature expiry. */
509 t = ns_get32(rdata); rdata += NS_INT32SZ;
510 len = SPRINTF((tmp, "%s ", p_secstodate(t)));
511 T(addstr(tmp, len, &buf, &buflen));
513 /* Time signed. */
514 t = ns_get32(rdata); rdata += NS_INT32SZ;
515 len = SPRINTF((tmp, "%s ", p_secstodate(t)));
516 T(addstr(tmp, len, &buf, &buflen));
518 /* Signature Footprint. */
519 footprint = ns_get16(rdata); rdata += NS_INT16SZ;
520 len = SPRINTF((tmp, "%u ", footprint));
521 T(addstr(tmp, len, &buf, &buflen));
523 /* Signer's name. */
524 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
526 /* Signature. */
527 len = b64_ntop(rdata, edata - rdata,
528 base64_key, sizeof base64_key);
529 if (len > 15) {
530 T(addstr(" (", 2, &buf, &buflen));
531 leader = "\n\t\t";
532 spaced = 0;
533 } else
534 leader = " ";
535 if (len < 0)
536 goto formerr;
537 for (n = 0; n < len; n += 48) {
538 T(addstr(leader, strlen(leader), &buf, &buflen));
539 T(addstr(base64_key + n, MIN(len - n, 48),
540 &buf, &buflen));
542 if (len > 15)
543 T(addstr(" )", 2, &buf, &buflen));
544 #endif /* !_LIBC */
545 break;
548 case ns_t_nxt: {
549 int n, c;
551 /* Next domain name. */
552 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
554 /* Type bit map. */
555 n = edata - rdata;
556 for (c = 0; c < n*8; c++)
557 if (NS_NXT_BIT_ISSET(c, rdata)) {
558 len = SPRINTF((tmp, " %s", p_type(c)));
559 T(addstr(tmp, len, &buf, &buflen));
561 break;
564 case ns_t_cert: {
565 u_int c_type, key_tag, alg;
566 int n, siz;
567 char base64_cert[8192], *leader, tmp[40];
569 c_type = ns_get16(rdata); rdata += NS_INT16SZ;
570 key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
571 alg = (u_int) *rdata++;
573 len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg));
574 T(addstr(tmp, len, &buf, &buflen));
575 siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
576 if (siz > sizeof(base64_cert) * 3/4) {
577 char *str = "record too long to print";
578 T(addstr(str, strlen(str), &buf, &buflen));
580 else {
581 len = b64_ntop(rdata, edata-rdata, base64_cert, siz);
583 if (len < 0)
584 goto formerr;
585 else if (len > 15) {
586 T(addstr(" (", 2, &buf, &buflen));
587 leader = "\n\t\t";
588 spaced = 0;
590 else
591 leader = " ";
593 for (n = 0; n < len; n += 48) {
594 T(addstr(leader, strlen(leader),
595 &buf, &buflen));
596 T(addstr(base64_cert + n, MIN(len - n, 48),
597 &buf, &buflen));
599 if (len > 15)
600 T(addstr(" )", 2, &buf, &buflen));
602 break;
605 case ns_t_tsig: {
606 /* BEW - need to complete this */
607 int n;
609 T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
610 T(addstr(" ", 1, &buf, &buflen));
611 rdata += 8; /* time */
612 n = ns_get16(rdata); rdata += INT16SZ;
613 rdata += n; /* sig */
614 n = ns_get16(rdata); rdata += INT16SZ; /* original id */
615 sprintf(buf, "%d", ns_get16(rdata));
616 rdata += INT16SZ;
617 addlen(strlen(buf), &buf, &buflen);
618 break;
621 default:
622 comment = "unknown RR type";
623 goto hexify;
625 return (buf - obuf);
626 formerr:
627 comment = "RR format error";
628 hexify: {
629 int n, m;
630 char *p;
632 len = SPRINTF((tmp, "\\#(\t\t; %s", comment));
633 T(addstr(tmp, len, &buf, &buflen));
634 while (rdata < edata) {
635 p = tmp;
636 p += SPRINTF((p, "\n\t"));
637 spaced = 0;
638 n = MIN(16, edata - rdata);
639 for (m = 0; m < n; m++)
640 p += SPRINTF((p, "%02x ", rdata[m]));
641 T(addstr(tmp, p - tmp, &buf, &buflen));
642 if (n < 16) {
643 T(addstr(")", 1, &buf, &buflen));
644 T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen));
646 p = tmp;
647 p += SPRINTF((p, "; "));
648 for (m = 0; m < n; m++)
649 *p++ = (isascii(rdata[m]) && isprint(rdata[m]))
650 ? rdata[m]
651 : '.';
652 T(addstr(tmp, p - tmp, &buf, &buflen));
653 rdata += n;
655 return (buf - obuf);
659 /* Private. */
662 * size_t
663 * prune_origin(name, origin)
664 * Find out if the name is at or under the current origin.
665 * return:
666 * Number of characters in name before start of origin,
667 * or length of name if origin does not match.
668 * notes:
669 * This function should share code with samedomain().
671 static size_t
672 prune_origin(const char *name, const char *origin) {
673 const char *oname = name;
675 while (*name != '\0') {
676 if (origin != NULL && ns_samename(name, origin) == 1)
677 return (name - oname - (name > oname));
678 while (*name != '\0') {
679 if (*name == '\\') {
680 name++;
681 /* XXX need to handle \nnn form. */
682 if (*name == '\0')
683 break;
684 } else if (*name == '.') {
685 name++;
686 break;
688 name++;
691 return (name - oname);
695 * int
696 * charstr(rdata, edata, buf, buflen)
697 * Format a <character-string> into the presentation buffer.
698 * return:
699 * Number of rdata octets consumed
700 * 0 for protocol format error
701 * -1 for output buffer error
702 * side effects:
703 * buffer is advanced on success.
705 static int
706 charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) {
707 const u_char *odata = rdata;
708 size_t save_buflen = *buflen;
709 char *save_buf = *buf;
711 if (addstr("\"", 1, buf, buflen) < 0)
712 goto enospc;
713 if (rdata < edata) {
714 int n = *rdata;
716 if (rdata + 1 + n <= edata) {
717 rdata++;
718 while (n-- > 0) {
719 if (strchr("\n\"\\", *rdata) != NULL)
720 if (addstr("\\", 1, buf, buflen) < 0)
721 goto enospc;
722 if (addstr((const char *)rdata, 1,
723 buf, buflen) < 0)
724 goto enospc;
725 rdata++;
729 if (addstr("\"", 1, buf, buflen) < 0)
730 goto enospc;
731 return (rdata - odata);
732 enospc:
733 __set_errno (ENOSPC);
734 *buf = save_buf;
735 *buflen = save_buflen;
736 return (-1);
739 static int
740 addname(const u_char *msg, size_t msglen,
741 const u_char **pp, const char *origin,
742 char **buf, size_t *buflen)
744 size_t newlen, save_buflen = *buflen;
745 char *save_buf = *buf;
746 int n;
748 n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen);
749 if (n < 0)
750 goto enospc; /* Guess. */
751 newlen = prune_origin(*buf, origin);
752 if ((origin == NULL || origin[0] == '\0' || (*buf)[newlen] == '\0') &&
753 (newlen == 0 || (*buf)[newlen - 1] != '.')) {
754 /* No trailing dot. */
755 if (newlen + 2 > *buflen)
756 goto enospc; /* No room for ".\0". */
757 (*buf)[newlen++] = '.';
758 (*buf)[newlen] = '\0';
760 if (newlen == 0) {
761 /* Use "@" instead of name. */
762 if (newlen + 2 > *buflen)
763 goto enospc; /* No room for "@\0". */
764 (*buf)[newlen++] = '@';
765 (*buf)[newlen] = '\0';
767 *pp += n;
768 addlen(newlen, buf, buflen);
769 **buf = '\0';
770 return (newlen);
771 enospc:
772 __set_errno (ENOSPC);
773 *buf = save_buf;
774 *buflen = save_buflen;
775 return (-1);
778 static void
779 addlen(size_t len, char **buf, size_t *buflen) {
780 #if 0
781 INSIST(len <= *buflen);
782 #endif
783 *buf += len;
784 *buflen -= len;
787 static int
788 addstr(const char *src, size_t len, char **buf, size_t *buflen) {
789 if (len >= *buflen) {
790 __set_errno (ENOSPC);
791 return (-1);
793 memcpy(*buf, src, len);
794 addlen(len, buf, buflen);
795 **buf = '\0';
796 return (0);
799 static int
800 addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) {
801 size_t save_buflen = *buflen;
802 char *save_buf = *buf;
803 int t;
805 if (spaced || len >= target - 1) {
806 T(addstr(" ", 2, buf, buflen));
807 spaced = 1;
808 } else {
809 for (t = (target - len - 1) / 8; t >= 0; t--)
810 if (addstr("\t", 1, buf, buflen) < 0) {
811 *buflen = save_buflen;
812 *buf = save_buf;
813 return (-1);
815 spaced = 0;
817 return (spaced);