Update.
[glibc.git] / resolv / ns_print.c
blob12b2e67ea66d1b3f3444c10bb9177d714a591167
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 /* 2*255 for hex digits, 128 for '.' and '\0', 2 for
313 0x if inet_nsap_ntoa starts using it. */
314 char t[255*2 + 128 + 2];
316 (void) inet_nsap_ntoa(rdlen, rdata, t);
317 T(addstr(t, strlen(t), &buf, &buflen));
318 break;
321 case ns_t_aaaa:
322 if (rdlen != NS_IN6ADDRSZ)
323 goto formerr;
324 (void) inet_ntop(AF_INET6, rdata, buf, buflen);
325 addlen(strlen(buf), &buf, &buflen);
326 break;
328 case ns_t_loc: {
329 char t[255];
331 /* XXX protocol format checking? */
332 (void) loc_ntoa(rdata, t);
333 T(addstr(t, strlen(t), &buf, &buflen));
334 break;
337 case ns_t_naptr: {
338 u_int order, preference;
339 char t[50];
341 if (rdlen < 2*NS_INT16SZ)
342 goto formerr;
344 /* Order, Precedence. */
345 order = ns_get16(rdata); rdata += NS_INT16SZ;
346 preference = ns_get16(rdata); rdata += NS_INT16SZ;
347 len = SPRINTF((t, "%u %u ", order, preference));
348 T(addstr(t, len, &buf, &buflen));
350 /* Flags. */
351 T(len = charstr(rdata, edata, &buf, &buflen));
352 if (len == 0)
353 goto formerr;
354 rdata += len;
355 T(addstr(" ", 1, &buf, &buflen));
357 /* Service. */
358 T(len = charstr(rdata, edata, &buf, &buflen));
359 if (len == 0)
360 goto formerr;
361 rdata += len;
362 T(addstr(" ", 1, &buf, &buflen));
364 /* Regexp. */
365 T(len = charstr(rdata, edata, &buf, &buflen));
366 if (len < 0)
367 return (-1);
368 if (len == 0)
369 goto formerr;
370 rdata += len;
371 T(addstr(" ", 1, &buf, &buflen));
373 /* Server. */
374 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
375 break;
378 case ns_t_srv: {
379 u_int priority, weight, port;
380 char t[50];
382 if (rdlen < NS_INT16SZ*3)
383 goto formerr;
385 /* Priority, Weight, Port. */
386 priority = ns_get16(rdata); rdata += NS_INT16SZ;
387 weight = ns_get16(rdata); rdata += NS_INT16SZ;
388 port = ns_get16(rdata); rdata += NS_INT16SZ;
389 len = SPRINTF((t, "%u %u %u ", priority, weight, port));
390 T(addstr(t, len, &buf, &buflen));
392 /* Server. */
393 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
394 break;
397 case ns_t_minfo:
398 case ns_t_rp:
399 /* Name1. */
400 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
401 T(addstr(" ", 1, &buf, &buflen));
403 /* Name2. */
404 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
406 break;
408 case ns_t_wks: {
409 int n, lcnt;
411 if (rdlen < NS_INT32SZ + 1)
412 goto formerr;
414 /* Address. */
415 (void) inet_ntop(AF_INET, rdata, buf, buflen);
416 addlen(strlen(buf), &buf, &buflen);
417 rdata += NS_INADDRSZ;
419 /* Protocol. */
420 len = SPRINTF((tmp, " %u ( ", *rdata));
421 T(addstr(tmp, len, &buf, &buflen));
422 rdata += NS_INT8SZ;
424 /* Bit map. */
425 n = 0;
426 lcnt = 0;
427 while (rdata < edata) {
428 u_int c = *rdata++;
429 do {
430 if (c & 0200) {
431 if (lcnt == 0) {
432 T(addstr("\n\t\t\t\t", 5,
433 &buf, &buflen));
434 lcnt = 10;
435 spaced = 0;
437 len = SPRINTF((tmp, "%d ", n));
438 T(addstr(tmp, len, &buf, &buflen));
439 lcnt--;
441 c <<= 1;
442 } while (++n & 07);
444 T(addstr(")", 1, &buf, &buflen));
446 break;
449 case ns_t_key: {
450 #ifndef _LIBC
451 char base64_key[NS_MD5RSA_MAX_BASE64];
452 u_int keyflags, protocol, algorithm, key_id;
453 const char *leader;
454 int n;
456 if (rdlen < NS_INT16SZ + NS_INT8SZ + NS_INT8SZ)
457 goto formerr;
459 /* Key flags, Protocol, Algorithm. */
460 key_id = dst_s_dns_key_id(rdata, edata-rdata);
461 keyflags = ns_get16(rdata); rdata += NS_INT16SZ;
462 protocol = *rdata++;
463 algorithm = *rdata++;
464 len = SPRINTF((tmp, "0x%04x %u %u",
465 keyflags, protocol, algorithm));
466 T(addstr(tmp, len, &buf, &buflen));
468 /* Public key data. */
469 len = b64_ntop(rdata, edata - rdata,
470 base64_key, sizeof base64_key);
471 if (len < 0)
472 goto formerr;
473 if (len > 15) {
474 T(addstr(" (", 2, &buf, &buflen));
475 leader = "\n\t\t";
476 spaced = 0;
477 } else
478 leader = " ";
479 for (n = 0; n < len; n += 48) {
480 T(addstr(leader, strlen(leader), &buf, &buflen));
481 T(addstr(base64_key + n, MIN(len - n, 48),
482 &buf, &buflen));
484 if (len > 15)
485 T(addstr(" )", 2, &buf, &buflen));
486 n = SPRINTF((tmp, " ; key_tag= %u", key_id));
487 T(addstr(tmp, n, &buf, &buflen));
488 #endif /* !_LIBC */
490 break;
493 case ns_t_sig: {
494 #ifndef _LIBC
495 char base64_key[NS_MD5RSA_MAX_BASE64];
496 u_int type, algorithm, labels, footprint;
497 const char *leader;
498 u_long t;
499 int n;
501 if (rdlen < 22)
502 goto formerr;
504 /* Type covered, Algorithm, Label count, Original TTL. */
505 type = ns_get16(rdata); rdata += NS_INT16SZ;
506 algorithm = *rdata++;
507 labels = *rdata++;
508 t = ns_get32(rdata); rdata += NS_INT32SZ;
509 len = SPRINTF((tmp, "%s %d %d %lu ",
510 p_type(type), algorithm, labels, t));
511 T(addstr(tmp, len, &buf, &buflen));
512 if (labels > (u_int)dn_count_labels(name))
513 goto formerr;
515 /* Signature expiry. */
516 t = ns_get32(rdata); rdata += NS_INT32SZ;
517 len = SPRINTF((tmp, "%s ", p_secstodate(t)));
518 T(addstr(tmp, len, &buf, &buflen));
520 /* Time signed. */
521 t = ns_get32(rdata); rdata += NS_INT32SZ;
522 len = SPRINTF((tmp, "%s ", p_secstodate(t)));
523 T(addstr(tmp, len, &buf, &buflen));
525 /* Signature Footprint. */
526 footprint = ns_get16(rdata); rdata += NS_INT16SZ;
527 len = SPRINTF((tmp, "%u ", footprint));
528 T(addstr(tmp, len, &buf, &buflen));
530 /* Signer's name. */
531 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
533 /* Signature. */
534 len = b64_ntop(rdata, edata - rdata,
535 base64_key, sizeof base64_key);
536 if (len > 15) {
537 T(addstr(" (", 2, &buf, &buflen));
538 leader = "\n\t\t";
539 spaced = 0;
540 } else
541 leader = " ";
542 if (len < 0)
543 goto formerr;
544 for (n = 0; n < len; n += 48) {
545 T(addstr(leader, strlen(leader), &buf, &buflen));
546 T(addstr(base64_key + n, MIN(len - n, 48),
547 &buf, &buflen));
549 if (len > 15)
550 T(addstr(" )", 2, &buf, &buflen));
551 #endif /* !_LIBC */
552 break;
555 case ns_t_nxt: {
556 int n, c;
558 /* Next domain name. */
559 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
561 /* Type bit map. */
562 n = edata - rdata;
563 for (c = 0; c < n*8; c++)
564 if (NS_NXT_BIT_ISSET(c, rdata)) {
565 len = SPRINTF((tmp, " %s", p_type(c)));
566 T(addstr(tmp, len, &buf, &buflen));
568 break;
571 case ns_t_cert: {
572 u_int c_type, key_tag, alg;
573 int n, siz;
574 char base64_cert[8192], *leader, tmp[40];
576 c_type = ns_get16(rdata); rdata += NS_INT16SZ;
577 key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
578 alg = (u_int) *rdata++;
580 len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg));
581 T(addstr(tmp, len, &buf, &buflen));
582 siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
583 if (siz > sizeof(base64_cert) * 3/4) {
584 char *str = "record too long to print";
585 T(addstr(str, strlen(str), &buf, &buflen));
587 else {
588 len = b64_ntop(rdata, edata-rdata, base64_cert, siz);
590 if (len < 0)
591 goto formerr;
592 else if (len > 15) {
593 T(addstr(" (", 2, &buf, &buflen));
594 leader = "\n\t\t";
595 spaced = 0;
597 else
598 leader = " ";
600 for (n = 0; n < len; n += 48) {
601 T(addstr(leader, strlen(leader),
602 &buf, &buflen));
603 T(addstr(base64_cert + n, MIN(len - n, 48),
604 &buf, &buflen));
606 if (len > 15)
607 T(addstr(" )", 2, &buf, &buflen));
609 break;
612 case ns_t_tsig: {
613 /* BEW - need to complete this */
614 int n;
616 T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
617 T(addstr(" ", 1, &buf, &buflen));
618 rdata += 8; /* time */
619 n = ns_get16(rdata); rdata += INT16SZ;
620 rdata += n; /* sig */
621 n = ns_get16(rdata); rdata += INT16SZ; /* original id */
622 sprintf(buf, "%d", ns_get16(rdata));
623 rdata += INT16SZ;
624 addlen(strlen(buf), &buf, &buflen);
625 break;
628 default:
629 comment = "unknown RR type";
630 goto hexify;
632 return (buf - obuf);
633 formerr:
634 comment = "RR format error";
635 hexify: {
636 int n, m;
637 char *p;
639 len = SPRINTF((tmp, "\\#(\t\t; %s", comment));
640 T(addstr(tmp, len, &buf, &buflen));
641 while (rdata < edata) {
642 p = tmp;
643 p += SPRINTF((p, "\n\t"));
644 spaced = 0;
645 n = MIN(16, edata - rdata);
646 for (m = 0; m < n; m++)
647 p += SPRINTF((p, "%02x ", rdata[m]));
648 T(addstr(tmp, p - tmp, &buf, &buflen));
649 if (n < 16) {
650 T(addstr(")", 1, &buf, &buflen));
651 T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen));
653 p = tmp;
654 p += SPRINTF((p, "; "));
655 for (m = 0; m < n; m++)
656 *p++ = (isascii(rdata[m]) && isprint(rdata[m]))
657 ? rdata[m]
658 : '.';
659 T(addstr(tmp, p - tmp, &buf, &buflen));
660 rdata += n;
662 return (buf - obuf);
666 /* Private. */
669 * size_t
670 * prune_origin(name, origin)
671 * Find out if the name is at or under the current origin.
672 * return:
673 * Number of characters in name before start of origin,
674 * or length of name if origin does not match.
675 * notes:
676 * This function should share code with samedomain().
678 static size_t
679 prune_origin(const char *name, const char *origin) {
680 const char *oname = name;
682 while (*name != '\0') {
683 if (origin != NULL && ns_samename(name, origin) == 1)
684 return (name - oname - (name > oname));
685 while (*name != '\0') {
686 if (*name == '\\') {
687 name++;
688 /* XXX need to handle \nnn form. */
689 if (*name == '\0')
690 break;
691 } else if (*name == '.') {
692 name++;
693 break;
695 name++;
698 return (name - oname);
702 * int
703 * charstr(rdata, edata, buf, buflen)
704 * Format a <character-string> into the presentation buffer.
705 * return:
706 * Number of rdata octets consumed
707 * 0 for protocol format error
708 * -1 for output buffer error
709 * side effects:
710 * buffer is advanced on success.
712 static int
713 charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) {
714 const u_char *odata = rdata;
715 size_t save_buflen = *buflen;
716 char *save_buf = *buf;
718 if (addstr("\"", 1, buf, buflen) < 0)
719 goto enospc;
720 if (rdata < edata) {
721 int n = *rdata;
723 if (rdata + 1 + n <= edata) {
724 rdata++;
725 while (n-- > 0) {
726 if (strchr("\n\"\\", *rdata) != NULL)
727 if (addstr("\\", 1, buf, buflen) < 0)
728 goto enospc;
729 if (addstr((const char *)rdata, 1,
730 buf, buflen) < 0)
731 goto enospc;
732 rdata++;
736 if (addstr("\"", 1, buf, buflen) < 0)
737 goto enospc;
738 return (rdata - odata);
739 enospc:
740 __set_errno (ENOSPC);
741 *buf = save_buf;
742 *buflen = save_buflen;
743 return (-1);
746 static int
747 addname(const u_char *msg, size_t msglen,
748 const u_char **pp, const char *origin,
749 char **buf, size_t *buflen)
751 size_t newlen, save_buflen = *buflen;
752 char *save_buf = *buf;
753 int n;
755 n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen);
756 if (n < 0)
757 goto enospc; /* Guess. */
758 newlen = prune_origin(*buf, origin);
759 if (newlen == 0) {
760 /* Use "@" instead of name. */
761 if (newlen + 2 > *buflen)
762 goto enospc; /* No room for "@\0". */
763 (*buf)[newlen++] = '@';
764 (*buf)[newlen] = '\0';
765 } else {
766 if (((origin == NULL || origin[0] == '\0') ||
767 (origin[0] != '.' && origin[1] != '\0' &&
768 (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') {
769 /* No trailing dot. */
770 if (newlen + 2 > *buflen)
771 goto enospc; /* No room for ".\0". */
772 (*buf)[newlen++] = '.';
773 (*buf)[newlen] = '\0';
776 *pp += n;
777 addlen(newlen, buf, buflen);
778 **buf = '\0';
779 return (newlen);
780 enospc:
781 __set_errno (ENOSPC);
782 *buf = save_buf;
783 *buflen = save_buflen;
784 return (-1);
787 static void
788 addlen(size_t len, char **buf, size_t *buflen) {
789 assert(len <= *buflen);
790 *buf += len;
791 *buflen -= len;
794 static int
795 addstr(const char *src, size_t len, char **buf, size_t *buflen) {
796 if (len >= *buflen) {
797 __set_errno (ENOSPC);
798 return (-1);
800 memcpy(*buf, src, len);
801 addlen(len, buf, buflen);
802 **buf = '\0';
803 return (0);
806 static int
807 addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) {
808 size_t save_buflen = *buflen;
809 char *save_buf = *buf;
810 int t;
812 if (spaced || len >= target - 1) {
813 T(addstr(" ", 2, buf, buflen));
814 spaced = 1;
815 } else {
816 for (t = (target - len - 1) / 8; t >= 0; t--)
817 if (addstr("\t", 1, buf, buflen) < 0) {
818 *buflen = save_buflen;
819 *buf = save_buf;
820 return (-1);
822 spaced = 0;
824 return (spaced);