Update.
[glibc.git] / resolv / ns_print.c
blob7a2ef70efc22f9280ffeb78ab3f41a8653f693de
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 #if !defined(_LIBC) && !defined(lint)
19 static const char rcsid[] = "$BINDId: ns_print.c,v 8.18 2000/02/29 05:48:12 vixie Exp $";
20 #endif
22 /* Import. */
24 #include <sys/types.h>
25 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <arpa/nameser.h>
29 #include <arpa/inet.h>
31 #include <assert.h>
32 #include <errno.h>
33 #include <resolv.h>
34 #include <string.h>
35 #include <ctype.h>
37 #ifdef SPRINTF_CHAR
38 # define SPRINTF(x) strlen(sprintf/**/x)
39 #else
40 # define SPRINTF(x) ((size_t)sprintf x)
41 #endif
43 /* Forward. */
45 static size_t prune_origin(const char *name, const char *origin);
46 static int charstr(const u_char *rdata, const u_char *edata,
47 char **buf, size_t *buflen);
48 static int addname(const u_char *msg, size_t msglen,
49 const u_char **p, const char *origin,
50 char **buf, size_t *buflen);
51 static void addlen(size_t len, char **buf, size_t *buflen);
52 static int addstr(const char *src, size_t len,
53 char **buf, size_t *buflen);
54 static int addtab(size_t len, size_t target, int spaced,
55 char **buf, size_t *buflen);
57 /* Proto. */
59 #ifndef _LIBC
60 u_int16_t dst_s_dns_key_id(const u_char *, const int);
61 #endif
63 /* Macros. */
65 #define T(x) \
66 do { \
67 if ((x) < 0) \
68 return (-1); \
69 } while (0)
71 /* Public. */
74 * int
75 * ns_sprintrr(handle, rr, name_ctx, origin, buf, buflen)
76 * Convert an RR to presentation format.
77 * return:
78 * Number of characters written to buf, or -1 (check errno).
80 int
81 ns_sprintrr(const ns_msg *handle, const ns_rr *rr,
82 const char *name_ctx, const char *origin,
83 char *buf, size_t buflen)
85 int n;
87 n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle),
88 ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr),
89 ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr),
90 name_ctx, origin, buf, buflen);
91 return (n);
95 * int
96 * ns_sprintrrf(msg, msglen, name, class, type, ttl, rdata, rdlen,
97 * name_ctx, origin, buf, buflen)
98 * Convert the fields of an RR into presentation format.
99 * return:
100 * Number of characters written to buf, or -1 (check errno).
103 ns_sprintrrf(const u_char *msg, size_t msglen,
104 const char *name, ns_class class, ns_type type,
105 u_long ttl, const u_char *rdata, size_t rdlen,
106 const char *name_ctx, const char *origin,
107 char *buf, size_t buflen)
109 const char *obuf = buf;
110 const u_char *edata = rdata + rdlen;
111 int spaced = 0;
113 const char *comment;
114 char tmp[100];
115 int len, x;
118 * Owner.
120 if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) {
121 T(addstr("\t\t\t", 3, &buf, &buflen));
122 } else {
123 len = prune_origin(name, origin);
124 if (len == 0) {
125 T(addstr("@\t\t\t", 4, &buf, &buflen));
126 } else {
127 T(addstr(name, len, &buf, &buflen));
128 /* Origin not used or not root, and no trailing dot? */
129 if (((origin == NULL || origin[0] == '\0') ||
130 (origin[0] != '.' && origin[1] != '\0' &&
131 name[len] == '\0')) && name[len - 1] != '.') {
132 T(addstr(".", 1, &buf, &buflen));
133 len++;
135 T(spaced = addtab(len, 24, spaced, &buf, &buflen));
140 * TTL, Class, Type.
142 T(x = ns_format_ttl(ttl, buf, buflen));
143 addlen(x, &buf, &buflen);
144 len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type)));
145 T(addstr(tmp, len, &buf, &buflen));
146 T(spaced = addtab(x + len, 16, spaced, &buf, &buflen));
149 * RData.
151 switch (type) {
152 case ns_t_a:
153 if (rdlen != NS_INADDRSZ)
154 goto formerr;
155 (void) inet_ntop(AF_INET, rdata, buf, buflen);
156 addlen(strlen(buf), &buf, &buflen);
157 break;
159 case ns_t_cname:
160 case ns_t_mb:
161 case ns_t_mg:
162 case ns_t_mr:
163 case ns_t_ns:
164 case ns_t_ptr:
165 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
166 break;
168 case ns_t_hinfo:
169 case ns_t_isdn:
170 /* First word. */
171 T(len = charstr(rdata, edata, &buf, &buflen));
172 if (len == 0)
173 goto formerr;
174 rdata += len;
175 T(addstr(" ", 1, &buf, &buflen));
178 /* Second word, optional in ISDN records. */
179 if (type == ns_t_isdn && rdata == edata)
180 break;
182 T(len = charstr(rdata, edata, &buf, &buflen));
183 if (len == 0)
184 goto formerr;
185 rdata += len;
186 break;
188 case ns_t_soa: {
189 u_long t;
191 /* Server name. */
192 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
193 T(addstr(" ", 1, &buf, &buflen));
195 /* Administrator name. */
196 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
197 T(addstr(" (\n", 3, &buf, &buflen));
198 spaced = 0;
200 if ((edata - rdata) != 5*NS_INT32SZ)
201 goto formerr;
203 /* Serial number. */
204 t = ns_get32(rdata); rdata += NS_INT32SZ;
205 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
206 len = SPRINTF((tmp, "%lu", t));
207 T(addstr(tmp, len, &buf, &buflen));
208 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
209 T(addstr("; serial\n", 9, &buf, &buflen));
210 spaced = 0;
212 /* Refresh interval. */
213 t = ns_get32(rdata); rdata += NS_INT32SZ;
214 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
215 T(len = ns_format_ttl(t, buf, buflen));
216 addlen(len, &buf, &buflen);
217 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
218 T(addstr("; refresh\n", 10, &buf, &buflen));
219 spaced = 0;
221 /* Retry interval. */
222 t = ns_get32(rdata); rdata += NS_INT32SZ;
223 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
224 T(len = ns_format_ttl(t, buf, buflen));
225 addlen(len, &buf, &buflen);
226 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
227 T(addstr("; retry\n", 8, &buf, &buflen));
228 spaced = 0;
230 /* Expiry. */
231 t = ns_get32(rdata); rdata += NS_INT32SZ;
232 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
233 T(len = ns_format_ttl(t, buf, buflen));
234 addlen(len, &buf, &buflen);
235 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
236 T(addstr("; expiry\n", 9, &buf, &buflen));
237 spaced = 0;
239 /* Minimum TTL. */
240 t = ns_get32(rdata); rdata += NS_INT32SZ;
241 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
242 T(len = ns_format_ttl(t, buf, buflen));
243 addlen(len, &buf, &buflen);
244 T(addstr(" )", 2, &buf, &buflen));
245 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
246 T(addstr("; minimum\n", 10, &buf, &buflen));
248 break;
251 case ns_t_mx:
252 case ns_t_afsdb:
253 case ns_t_rt: {
254 u_int t;
256 if (rdlen < NS_INT16SZ)
257 goto formerr;
259 /* Priority. */
260 t = ns_get16(rdata);
261 rdata += NS_INT16SZ;
262 len = SPRINTF((tmp, "%u ", t));
263 T(addstr(tmp, len, &buf, &buflen));
265 /* Target. */
266 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
268 break;
271 case ns_t_px: {
272 u_int t;
274 if (rdlen < NS_INT16SZ)
275 goto formerr;
277 /* Priority. */
278 t = ns_get16(rdata);
279 rdata += NS_INT16SZ;
280 len = SPRINTF((tmp, "%u ", t));
281 T(addstr(tmp, len, &buf, &buflen));
283 /* Name1. */
284 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
285 T(addstr(" ", 1, &buf, &buflen));
287 /* Name2. */
288 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
290 break;
293 case ns_t_x25:
294 T(len = charstr(rdata, edata, &buf, &buflen));
295 if (len == 0)
296 goto formerr;
297 rdata += len;
298 break;
300 case ns_t_txt:
301 while (rdata < edata) {
302 T(len = charstr(rdata, edata, &buf, &buflen));
303 if (len == 0)
304 goto formerr;
305 rdata += len;
306 if (rdata < edata)
307 T(addstr(" ", 1, &buf, &buflen));
309 break;
311 case ns_t_nsap: {
312 char t[255*3];
314 (void) inet_nsap_ntoa(rdlen, rdata, t);
315 T(addstr(t, strlen(t), &buf, &buflen));
316 break;
319 case ns_t_aaaa:
320 if (rdlen != NS_IN6ADDRSZ)
321 goto formerr;
322 (void) inet_ntop(AF_INET6, rdata, buf, buflen);
323 addlen(strlen(buf), &buf, &buflen);
324 break;
326 case ns_t_loc: {
327 char t[255];
329 /* XXX protocol format checking? */
330 (void) loc_ntoa(rdata, t);
331 T(addstr(t, strlen(t), &buf, &buflen));
332 break;
335 case ns_t_naptr: {
336 u_int order, preference;
337 char t[50];
339 if (rdlen < 2*NS_INT16SZ)
340 goto formerr;
342 /* Order, Precedence. */
343 order = ns_get16(rdata); rdata += NS_INT16SZ;
344 preference = ns_get16(rdata); rdata += NS_INT16SZ;
345 len = SPRINTF((t, "%u %u ", order, preference));
346 T(addstr(t, len, &buf, &buflen));
348 /* Flags. */
349 T(len = charstr(rdata, edata, &buf, &buflen));
350 if (len == 0)
351 goto formerr;
352 rdata += len;
353 T(addstr(" ", 1, &buf, &buflen));
355 /* Service. */
356 T(len = charstr(rdata, edata, &buf, &buflen));
357 if (len == 0)
358 goto formerr;
359 rdata += len;
360 T(addstr(" ", 1, &buf, &buflen));
362 /* Regexp. */
363 T(len = charstr(rdata, edata, &buf, &buflen));
364 if (len < 0)
365 return (-1);
366 if (len == 0)
367 goto formerr;
368 rdata += len;
369 T(addstr(" ", 1, &buf, &buflen));
371 /* Server. */
372 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
373 break;
376 case ns_t_srv: {
377 u_int priority, weight, port;
378 char t[50];
380 if (rdlen < NS_INT16SZ*3)
381 goto formerr;
383 /* Priority, Weight, Port. */
384 priority = ns_get16(rdata); rdata += NS_INT16SZ;
385 weight = ns_get16(rdata); rdata += NS_INT16SZ;
386 port = ns_get16(rdata); rdata += NS_INT16SZ;
387 len = SPRINTF((t, "%u %u %u ", priority, weight, port));
388 T(addstr(t, len, &buf, &buflen));
390 /* Server. */
391 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
392 break;
395 case ns_t_minfo:
396 case ns_t_rp:
397 /* Name1. */
398 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
399 T(addstr(" ", 1, &buf, &buflen));
401 /* Name2. */
402 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
404 break;
406 case ns_t_wks: {
407 int n, lcnt;
409 if (rdlen < NS_INT32SZ + 1)
410 goto formerr;
412 /* Address. */
413 (void) inet_ntop(AF_INET, rdata, buf, buflen);
414 addlen(strlen(buf), &buf, &buflen);
415 rdata += NS_INADDRSZ;
417 /* Protocol. */
418 len = SPRINTF((tmp, " %u ( ", *rdata));
419 T(addstr(tmp, len, &buf, &buflen));
420 rdata += NS_INT8SZ;
422 /* Bit map. */
423 n = 0;
424 lcnt = 0;
425 while (rdata < edata) {
426 u_int c = *rdata++;
427 do {
428 if (c & 0200) {
429 if (lcnt == 0) {
430 T(addstr("\n\t\t\t\t", 5,
431 &buf, &buflen));
432 lcnt = 10;
433 spaced = 0;
435 len = SPRINTF((tmp, "%d ", n));
436 T(addstr(tmp, len, &buf, &buflen));
437 lcnt--;
439 c <<= 1;
440 } while (++n & 07);
442 T(addstr(")", 1, &buf, &buflen));
444 break;
447 case ns_t_key: {
448 #ifndef _LIBC
449 char base64_key[NS_MD5RSA_MAX_BASE64];
450 u_int keyflags, protocol, algorithm, key_id;
451 const char *leader;
452 int n;
454 if (rdlen < NS_INT16SZ + NS_INT8SZ + NS_INT8SZ)
455 goto formerr;
457 /* Key flags, Protocol, Algorithm. */
458 key_id = dst_s_dns_key_id(rdata, edata-rdata);
459 keyflags = ns_get16(rdata); rdata += NS_INT16SZ;
460 protocol = *rdata++;
461 algorithm = *rdata++;
462 len = SPRINTF((tmp, "0x%04x %u %u",
463 keyflags, protocol, algorithm));
464 T(addstr(tmp, len, &buf, &buflen));
466 /* Public key data. */
467 len = b64_ntop(rdata, edata - rdata,
468 base64_key, sizeof base64_key);
469 if (len < 0)
470 goto formerr;
471 if (len > 15) {
472 T(addstr(" (", 2, &buf, &buflen));
473 leader = "\n\t\t";
474 spaced = 0;
475 } else
476 leader = " ";
477 for (n = 0; n < len; n += 48) {
478 T(addstr(leader, strlen(leader), &buf, &buflen));
479 T(addstr(base64_key + n, MIN(len - n, 48),
480 &buf, &buflen));
482 if (len > 15)
483 T(addstr(" )", 2, &buf, &buflen));
484 n = SPRINTF((tmp, " ; key_tag= %u", key_id));
485 T(addstr(tmp, n, &buf, &buflen));
486 #endif /* !_LIBC */
488 break;
491 case ns_t_sig: {
492 #ifndef _LIBC
493 char base64_key[NS_MD5RSA_MAX_BASE64];
494 u_int type, algorithm, labels, footprint;
495 const char *leader;
496 u_long t;
497 int n;
499 if (rdlen < 22)
500 goto formerr;
502 /* Type covered, Algorithm, Label count, Original TTL. */
503 type = ns_get16(rdata); rdata += NS_INT16SZ;
504 algorithm = *rdata++;
505 labels = *rdata++;
506 t = ns_get32(rdata); rdata += NS_INT32SZ;
507 len = SPRINTF((tmp, "%s %d %d %lu ",
508 p_type(type), algorithm, labels, t));
509 T(addstr(tmp, len, &buf, &buflen));
510 if (labels > (u_int)dn_count_labels(name))
511 goto formerr;
513 /* Signature expiry. */
514 t = ns_get32(rdata); rdata += NS_INT32SZ;
515 len = SPRINTF((tmp, "%s ", p_secstodate(t)));
516 T(addstr(tmp, len, &buf, &buflen));
518 /* Time signed. */
519 t = ns_get32(rdata); rdata += NS_INT32SZ;
520 len = SPRINTF((tmp, "%s ", p_secstodate(t)));
521 T(addstr(tmp, len, &buf, &buflen));
523 /* Signature Footprint. */
524 footprint = ns_get16(rdata); rdata += NS_INT16SZ;
525 len = SPRINTF((tmp, "%u ", footprint));
526 T(addstr(tmp, len, &buf, &buflen));
528 /* Signer's name. */
529 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
531 /* Signature. */
532 len = b64_ntop(rdata, edata - rdata,
533 base64_key, sizeof base64_key);
534 if (len > 15) {
535 T(addstr(" (", 2, &buf, &buflen));
536 leader = "\n\t\t";
537 spaced = 0;
538 } else
539 leader = " ";
540 if (len < 0)
541 goto formerr;
542 for (n = 0; n < len; n += 48) {
543 T(addstr(leader, strlen(leader), &buf, &buflen));
544 T(addstr(base64_key + n, MIN(len - n, 48),
545 &buf, &buflen));
547 if (len > 15)
548 T(addstr(" )", 2, &buf, &buflen));
549 #endif /* !_LIBC */
550 break;
553 case ns_t_nxt: {
554 int n, c;
556 /* Next domain name. */
557 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
559 /* Type bit map. */
560 n = edata - rdata;
561 for (c = 0; c < n*8; c++)
562 if (NS_NXT_BIT_ISSET(c, rdata)) {
563 len = SPRINTF((tmp, " %s", p_type(c)));
564 T(addstr(tmp, len, &buf, &buflen));
566 break;
569 case ns_t_cert: {
570 u_int c_type, key_tag, alg;
571 int n, siz;
572 char base64_cert[8192], *leader, tmp[40];
574 c_type = ns_get16(rdata); rdata += NS_INT16SZ;
575 key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
576 alg = (u_int) *rdata++;
578 len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg));
579 T(addstr(tmp, len, &buf, &buflen));
580 siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
581 if (siz > sizeof(base64_cert) * 3/4) {
582 char *str = "record too long to print";
583 T(addstr(str, strlen(str), &buf, &buflen));
585 else {
586 len = b64_ntop(rdata, edata-rdata, base64_cert, siz);
588 if (len < 0)
589 goto formerr;
590 else if (len > 15) {
591 T(addstr(" (", 2, &buf, &buflen));
592 leader = "\n\t\t";
593 spaced = 0;
595 else
596 leader = " ";
598 for (n = 0; n < len; n += 48) {
599 T(addstr(leader, strlen(leader),
600 &buf, &buflen));
601 T(addstr(base64_cert + n, MIN(len - n, 48),
602 &buf, &buflen));
604 if (len > 15)
605 T(addstr(" )", 2, &buf, &buflen));
607 break;
610 case ns_t_tsig: {
611 /* BEW - need to complete this */
612 int n;
614 T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
615 T(addstr(" ", 1, &buf, &buflen));
616 rdata += 8; /* time */
617 n = ns_get16(rdata); rdata += INT16SZ;
618 rdata += n; /* sig */
619 n = ns_get16(rdata); rdata += INT16SZ; /* original id */
620 sprintf(buf, "%d", ns_get16(rdata));
621 rdata += INT16SZ;
622 addlen(strlen(buf), &buf, &buflen);
623 break;
626 default:
627 comment = "unknown RR type";
628 goto hexify;
630 return (buf - obuf);
631 formerr:
632 comment = "RR format error";
633 hexify: {
634 int n, m;
635 char *p;
637 len = SPRINTF((tmp, "\\#(\t\t; %s", comment));
638 T(addstr(tmp, len, &buf, &buflen));
639 while (rdata < edata) {
640 p = tmp;
641 p += SPRINTF((p, "\n\t"));
642 spaced = 0;
643 n = MIN(16, edata - rdata);
644 for (m = 0; m < n; m++)
645 p += SPRINTF((p, "%02x ", rdata[m]));
646 T(addstr(tmp, p - tmp, &buf, &buflen));
647 if (n < 16) {
648 T(addstr(")", 1, &buf, &buflen));
649 T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen));
651 p = tmp;
652 p += SPRINTF((p, "; "));
653 for (m = 0; m < n; m++)
654 *p++ = (isascii(rdata[m]) && isprint(rdata[m]))
655 ? rdata[m]
656 : '.';
657 T(addstr(tmp, p - tmp, &buf, &buflen));
658 rdata += n;
660 return (buf - obuf);
664 /* Private. */
667 * size_t
668 * prune_origin(name, origin)
669 * Find out if the name is at or under the current origin.
670 * return:
671 * Number of characters in name before start of origin,
672 * or length of name if origin does not match.
673 * notes:
674 * This function should share code with samedomain().
676 static size_t
677 prune_origin(const char *name, const char *origin) {
678 const char *oname = name;
680 while (*name != '\0') {
681 if (origin != NULL && ns_samename(name, origin) == 1)
682 return (name - oname - (name > oname));
683 while (*name != '\0') {
684 if (*name == '\\') {
685 name++;
686 /* XXX need to handle \nnn form. */
687 if (*name == '\0')
688 break;
689 } else if (*name == '.') {
690 name++;
691 break;
693 name++;
696 return (name - oname);
700 * int
701 * charstr(rdata, edata, buf, buflen)
702 * Format a <character-string> into the presentation buffer.
703 * return:
704 * Number of rdata octets consumed
705 * 0 for protocol format error
706 * -1 for output buffer error
707 * side effects:
708 * buffer is advanced on success.
710 static int
711 charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) {
712 const u_char *odata = rdata;
713 size_t save_buflen = *buflen;
714 char *save_buf = *buf;
716 if (addstr("\"", 1, buf, buflen) < 0)
717 goto enospc;
718 if (rdata < edata) {
719 int n = *rdata;
721 if (rdata + 1 + n <= edata) {
722 rdata++;
723 while (n-- > 0) {
724 if (strchr("\n\"\\", *rdata) != NULL)
725 if (addstr("\\", 1, buf, buflen) < 0)
726 goto enospc;
727 if (addstr((const char *)rdata, 1,
728 buf, buflen) < 0)
729 goto enospc;
730 rdata++;
734 if (addstr("\"", 1, buf, buflen) < 0)
735 goto enospc;
736 return (rdata - odata);
737 enospc:
738 __set_errno (ENOSPC);
739 *buf = save_buf;
740 *buflen = save_buflen;
741 return (-1);
744 static int
745 addname(const u_char *msg, size_t msglen,
746 const u_char **pp, const char *origin,
747 char **buf, size_t *buflen)
749 size_t newlen, save_buflen = *buflen;
750 char *save_buf = *buf;
751 int n;
753 n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen);
754 if (n < 0)
755 goto enospc; /* Guess. */
756 newlen = prune_origin(*buf, origin);
757 if (newlen == 0) {
758 /* Use "@" instead of name. */
759 if (newlen + 2 > *buflen)
760 goto enospc; /* No room for "@\0". */
761 (*buf)[newlen++] = '@';
762 (*buf)[newlen] = '\0';
763 } else {
764 if (((origin == NULL || origin[0] == '\0') ||
765 (origin[0] != '.' && origin[1] != '\0' &&
766 (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') {
767 /* No trailing dot. */
768 if (newlen + 2 > *buflen)
769 goto enospc; /* No room for ".\0". */
770 (*buf)[newlen++] = '.';
771 (*buf)[newlen] = '\0';
774 *pp += n;
775 addlen(newlen, buf, buflen);
776 **buf = '\0';
777 return (newlen);
778 enospc:
779 __set_errno (ENOSPC);
780 *buf = save_buf;
781 *buflen = save_buflen;
782 return (-1);
785 static void
786 addlen(size_t len, char **buf, size_t *buflen) {
787 assert(len <= *buflen);
788 *buf += len;
789 *buflen -= len;
792 static int
793 addstr(const char *src, size_t len, char **buf, size_t *buflen) {
794 if (len >= *buflen) {
795 __set_errno (ENOSPC);
796 return (-1);
798 memcpy(*buf, src, len);
799 addlen(len, buf, buflen);
800 **buf = '\0';
801 return (0);
804 static int
805 addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) {
806 size_t save_buflen = *buflen;
807 char *save_buf = *buf;
808 int t;
810 if (spaced || len >= target - 1) {
811 T(addstr(" ", 2, buf, buflen));
812 spaced = 1;
813 } else {
814 for (t = (target - len - 1) / 8; t >= 0; t--)
815 if (addstr("\t", 1, buf, buflen) < 0) {
816 *buflen = save_buflen;
817 *buf = save_buf;
818 return (-1);
820 spaced = 0;
822 return (spaced);