1/*
2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1996-1999 by Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
10 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
11 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
12 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
15 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
16 * SOFTWARE.
17 */
18
19/* Import. */
20
21#include <sys/types.h>
22#include <sys/socket.h>
23
24#include <netinet/in.h>
25#include <arpa/nameser.h>
26#include <arpa/inet.h>
27
28#include <assert.h>
29#include <errno.h>
30#include <resolv.h>
31#include <string.h>
32#include <ctype.h>
33
34#define SPRINTF(x) ((size_t)sprintf x)
35
36/* Forward. */
37
38static size_t prune_origin(const char *name, const char *origin);
39static int charstr(const u_char *rdata, const u_char *edata,
40 char **buf, size_t *buflen);
41static int addname(const u_char *msg, size_t msglen,
42 const u_char **p, const char *origin,
43 char **buf, size_t *buflen);
44static void addlen(size_t len, char **buf, size_t *buflen);
45static int addstr(const char *src, size_t len,
46 char **buf, size_t *buflen);
47static int addtab(size_t len, size_t target, int spaced,
48 char **buf, size_t *buflen);
49
50/* Macros. */
51
52#define T(x) \
53 do { \
54 if ((x) < 0) \
55 return (-1); \
56 } while (0)
57
58/* Public. */
59
60/*%
61 * Convert an RR to presentation format.
62 *
63 * return:
64 *\li Number of characters written to buf, or -1 (check errno).
65 */
66int
67ns_sprintrr(const ns_msg *handle, const ns_rr *rr,
68 const char *name_ctx, const char *origin,
69 char *buf, size_t buflen)
70{
71 int n;
72
73 n = ns_sprintrrf(ns_msg_base(*handle), ns_msg_size(*handle),
74 ns_rr_name(*rr), ns_rr_class(*rr), ns_rr_type(*rr),
75 ns_rr_ttl(*rr), ns_rr_rdata(*rr), ns_rr_rdlen(*rr),
76 name_ctx, origin, buf, buflen);
77 return (n);
78}
79libresolv_hidden_def (ns_sprintrr)
80
81/*%
82 * Convert the fields of an RR into presentation format.
83 *
84 * return:
85 *\li Number of characters written to buf, or -1 (check errno).
86 */
87int
88ns_sprintrrf(const u_char *msg, size_t msglen,
89 const char *name, ns_class class, ns_type type,
90 u_long ttl, const u_char *rdata, size_t rdlen,
91 const char *name_ctx, const char *origin,
92 char *buf, size_t buflen)
93{
94 const char *obuf = buf;
95 const u_char *edata = rdata + rdlen;
96 int spaced = 0;
97
98 const char *comment;
99 char tmp[100];
100 char errbuf[40];
101 int len, x;
102
103 /*
104 * Owner.
105 */
106 if (name_ctx != NULL && ns_samename(name_ctx, name) == 1) {
107 T(addstr("\t\t\t", 3, &buf, &buflen));
108 } else {
109 len = prune_origin(name, origin);
110 if (*name == '\0') {
111 goto root;
112 } else if (len == 0) {
113 T(addstr("@\t\t\t", 4, &buf, &buflen));
114 } else {
115 T(addstr(name, len, &buf, &buflen));
116 /* Origin not used or not root, and no trailing dot? */
117 if (((origin == NULL || origin[0] == '\0') ||
118 (origin[0] != '.' && origin[1] != '\0' &&
119 name[len] == '\0')) && name[len - 1] != '.') {
120 root:
121 T(addstr(".", 1, &buf, &buflen));
122 len++;
123 }
124 T(spaced = addtab(len, 24, spaced, &buf, &buflen));
125 }
126 }
127
128 /*
129 * TTL, Class, Type.
130 */
131 T(x = ns_format_ttl(ttl, buf, buflen));
132 addlen(x, &buf, &buflen);
133 len = SPRINTF((tmp, " %s %s", p_class(class), p_type(type)));
134 T(addstr(tmp, len, &buf, &buflen));
135 T(spaced = addtab(x + len, 16, spaced, &buf, &buflen));
136
137 /*
138 * RData.
139 */
140 switch (type) {
141 case ns_t_a:
142 if (rdlen != (size_t)NS_INADDRSZ)
143 goto formerr;
144 (void) inet_ntop(AF_INET, rdata, buf, buflen);
145 addlen(strlen(buf), &buf, &buflen);
146 break;
147
148 case ns_t_cname:
149 case ns_t_mb:
150 case ns_t_mg:
151 case ns_t_mr:
152 case ns_t_ns:
153 case ns_t_ptr:
154 case ns_t_dname:
155 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
156 break;
157
158 case ns_t_hinfo:
159 case ns_t_isdn:
160 /* First word. */
161 T(len = charstr(rdata, edata, &buf, &buflen));
162 if (len == 0)
163 goto formerr;
164 rdata += len;
165 T(addstr(" ", 1, &buf, &buflen));
166
167
168 /* Second word, optional in ISDN records. */
169 if (type == ns_t_isdn && rdata == edata)
170 break;
171
172 T(len = charstr(rdata, edata, &buf, &buflen));
173 if (len == 0)
174 goto formerr;
175 rdata += len;
176 break;
177
178 case ns_t_soa: {
179 u_long t;
180
181 /* Server name. */
182 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
183 T(addstr(" ", 1, &buf, &buflen));
184
185 /* Administrator name. */
186 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
187 T(addstr(" (\n", 3, &buf, &buflen));
188 spaced = 0;
189
190 if ((edata - rdata) != 5*NS_INT32SZ)
191 goto formerr;
192
193 /* Serial number. */
194 t = ns_get32(rdata); rdata += NS_INT32SZ;
195 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
196 len = SPRINTF((tmp, "%lu", t));
197 T(addstr(tmp, len, &buf, &buflen));
198 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
199 T(addstr("; serial\n", 9, &buf, &buflen));
200 spaced = 0;
201
202 /* Refresh interval. */
203 t = ns_get32(rdata); rdata += NS_INT32SZ;
204 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
205 T(len = ns_format_ttl(t, buf, buflen));
206 addlen(len, &buf, &buflen);
207 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
208 T(addstr("; refresh\n", 10, &buf, &buflen));
209 spaced = 0;
210
211 /* Retry interval. */
212 t = ns_get32(rdata); rdata += NS_INT32SZ;
213 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
214 T(len = ns_format_ttl(t, buf, buflen));
215 addlen(len, &buf, &buflen);
216 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
217 T(addstr("; retry\n", 8, &buf, &buflen));
218 spaced = 0;
219
220 /* Expiry. */
221 t = ns_get32(rdata); rdata += NS_INT32SZ;
222 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
223 T(len = ns_format_ttl(t, buf, buflen));
224 addlen(len, &buf, &buflen);
225 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
226 T(addstr("; expiry\n", 9, &buf, &buflen));
227 spaced = 0;
228
229 /* Minimum TTL. */
230 t = ns_get32(rdata); rdata += NS_INT32SZ;
231 T(addstr("\t\t\t\t\t", 5, &buf, &buflen));
232 T(len = ns_format_ttl(t, buf, buflen));
233 addlen(len, &buf, &buflen);
234 T(addstr(" )", 2, &buf, &buflen));
235 T(spaced = addtab(len, 16, spaced, &buf, &buflen));
236 T(addstr("; minimum\n", 10, &buf, &buflen));
237
238 break;
239 }
240
241 case ns_t_mx:
242 case ns_t_afsdb:
243 case ns_t_rt: {
244 u_int t;
245
246 if (rdlen < (size_t)NS_INT16SZ)
247 goto formerr;
248
249 /* Priority. */
250 t = ns_get16(rdata);
251 rdata += NS_INT16SZ;
252 len = SPRINTF((tmp, "%u ", t));
253 T(addstr(tmp, len, &buf, &buflen));
254
255 /* Target. */
256 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
257
258 break;
259 }
260
261 case ns_t_px: {
262 u_int t;
263
264 if (rdlen < (size_t)NS_INT16SZ)
265 goto formerr;
266
267 /* Priority. */
268 t = ns_get16(rdata);
269 rdata += NS_INT16SZ;
270 len = SPRINTF((tmp, "%u ", t));
271 T(addstr(tmp, len, &buf, &buflen));
272
273 /* Name1. */
274 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
275 T(addstr(" ", 1, &buf, &buflen));
276
277 /* Name2. */
278 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
279
280 break;
281 }
282
283 case ns_t_x25:
284 T(len = charstr(rdata, edata, &buf, &buflen));
285 if (len == 0)
286 goto formerr;
287 rdata += len;
288 break;
289
290 case ns_t_txt:
291 while (rdata < edata) {
292 T(len = charstr(rdata, edata, &buf, &buflen));
293 if (len == 0)
294 goto formerr;
295 rdata += len;
296 if (rdata < edata)
297 T(addstr(" ", 1, &buf, &buflen));
298 }
299 break;
300
301 case ns_t_nsap: {
302 char t[2+255*3];
303
304 (void) inet_nsap_ntoa(rdlen, rdata, t);
305 T(addstr(t, strlen(t), &buf, &buflen));
306 break;
307 }
308
309 case ns_t_aaaa:
310 if (rdlen != (size_t)NS_IN6ADDRSZ)
311 goto formerr;
312 (void) inet_ntop(AF_INET6, rdata, buf, buflen);
313 addlen(strlen(buf), &buf, &buflen);
314 break;
315
316 case ns_t_loc: {
317 char t[255];
318
319 /* XXX protocol format checking? */
320 (void) loc_ntoa(rdata, t);
321 T(addstr(t, strlen(t), &buf, &buflen));
322 break;
323 }
324
325 case ns_t_naptr: {
326 u_int order, preference;
327 char t[50];
328
329 if (rdlen < 2U*NS_INT16SZ)
330 goto formerr;
331
332 /* Order, Precedence. */
333 order = ns_get16(rdata); rdata += NS_INT16SZ;
334 preference = ns_get16(rdata); rdata += NS_INT16SZ;
335 len = SPRINTF((t, "%u %u ", order, preference));
336 T(addstr(t, len, &buf, &buflen));
337
338 /* Flags. */
339 T(len = charstr(rdata, edata, &buf, &buflen));
340 if (len == 0)
341 goto formerr;
342 rdata += len;
343 T(addstr(" ", 1, &buf, &buflen));
344
345 /* Service. */
346 T(len = charstr(rdata, edata, &buf, &buflen));
347 if (len == 0)
348 goto formerr;
349 rdata += len;
350 T(addstr(" ", 1, &buf, &buflen));
351
352 /* Regexp. */
353 T(len = charstr(rdata, edata, &buf, &buflen));
354 if (len < 0)
355 return (-1);
356 if (len == 0)
357 goto formerr;
358 rdata += len;
359 T(addstr(" ", 1, &buf, &buflen));
360
361 /* Server. */
362 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
363 break;
364 }
365
366 case ns_t_srv: {
367 u_int priority, weight, port;
368 char t[50];
369
370 if (rdlen < 3U*NS_INT16SZ)
371 goto formerr;
372
373 /* Priority, Weight, Port. */
374 priority = ns_get16(rdata); rdata += NS_INT16SZ;
375 weight = ns_get16(rdata); rdata += NS_INT16SZ;
376 port = ns_get16(rdata); rdata += NS_INT16SZ;
377 len = SPRINTF((t, "%u %u %u ", priority, weight, port));
378 T(addstr(t, len, &buf, &buflen));
379
380 /* Server. */
381 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
382 break;
383 }
384
385 case ns_t_minfo:
386 case ns_t_rp:
387 /* Name1. */
388 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
389 T(addstr(" ", 1, &buf, &buflen));
390
391 /* Name2. */
392 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
393
394 break;
395
396 case ns_t_wks: {
397 int n, lcnt;
398
399 if (rdlen < 1U + NS_INT32SZ)
400 goto formerr;
401
402 /* Address. */
403 (void) inet_ntop(AF_INET, rdata, buf, buflen);
404 addlen(strlen(buf), &buf, &buflen);
405 rdata += NS_INADDRSZ;
406
407 /* Protocol. */
408 len = SPRINTF((tmp, " %u ( ", *rdata));
409 T(addstr(tmp, len, &buf, &buflen));
410 rdata += NS_INT8SZ;
411
412 /* Bit map. */
413 n = 0;
414 lcnt = 0;
415 while (rdata < edata) {
416 u_int c = *rdata++;
417 do {
418 if (c & 0200) {
419 if (lcnt == 0) {
420 T(addstr("\n\t\t\t\t", 5,
421 &buf, &buflen));
422 lcnt = 10;
423 spaced = 0;
424 }
425 len = SPRINTF((tmp, "%d ", n));
426 T(addstr(tmp, len, &buf, &buflen));
427 lcnt--;
428 }
429 c <<= 1;
430 } while (++n & 07);
431 }
432 T(addstr(")", 1, &buf, &buflen));
433
434 break;
435 }
436
437 case ns_t_cert: {
438 u_int c_type, key_tag, alg;
439 int n;
440 unsigned int siz;
441 char base64_cert[8192], tmp[40];
442 const char *leader;
443
444 c_type = ns_get16(rdata); rdata += NS_INT16SZ;
445 key_tag = ns_get16(rdata); rdata += NS_INT16SZ;
446 alg = (u_int) *rdata++;
447
448 len = SPRINTF((tmp, "%d %d %d ", c_type, key_tag, alg));
449 T(addstr(tmp, len, &buf, &buflen));
450 siz = (edata-rdata)*4/3 + 4; /* "+4" accounts for trailing \0 */
451 if (siz > sizeof(base64_cert) * 3/4) {
452 const char *str = "record too long to print";
453 T(addstr(str, strlen(str), &buf, &buflen));
454 }
455 else {
456 len = b64_ntop(rdata, edata-rdata, base64_cert, siz);
457
458 if (len < 0)
459 goto formerr;
460 else if (len > 15) {
461 T(addstr(" (", 2, &buf, &buflen));
462 leader = "\n\t\t";
463 spaced = 0;
464 }
465 else
466 leader = " ";
467
468 for (n = 0; n < len; n += 48) {
469 T(addstr(leader, strlen(leader),
470 &buf, &buflen));
471 T(addstr(base64_cert + n, MIN(len - n, 48),
472 &buf, &buflen));
473 }
474 if (len > 15)
475 T(addstr(" )", 2, &buf, &buflen));
476 }
477 break;
478 }
479
480 case ns_t_tkey: {
481 /* KJD - need to complete this */
482 u_long t;
483 int mode, err, keysize;
484
485 /* Algorithm name. */
486 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
487 T(addstr(" ", 1, &buf, &buflen));
488
489 /* Inception. */
490 t = ns_get32(rdata); rdata += NS_INT32SZ;
491 len = SPRINTF((tmp, "%lu ", t));
492 T(addstr(tmp, len, &buf, &buflen));
493
494 /* Experation. */
495 t = ns_get32(rdata); rdata += NS_INT32SZ;
496 len = SPRINTF((tmp, "%lu ", t));
497 T(addstr(tmp, len, &buf, &buflen));
498
499 /* Mode , Error, Key Size. */
500 /* Priority, Weight, Port. */
501 mode = ns_get16(rdata); rdata += NS_INT16SZ;
502 err = ns_get16(rdata); rdata += NS_INT16SZ;
503 keysize = ns_get16(rdata); rdata += NS_INT16SZ;
504 len = SPRINTF((tmp, "%u %u %u ", mode, err, keysize));
505 T(addstr(tmp, len, &buf, &buflen));
506
507 /* XXX need to dump key, print otherdata length & other data */
508 break;
509 }
510
511 case ns_t_tsig: {
512 /* BEW - need to complete this */
513 int n;
514
515 T(len = addname(msg, msglen, &rdata, origin, &buf, &buflen));
516 T(addstr(" ", 1, &buf, &buflen));
517 rdata += 8; /*%< time */
518 n = ns_get16(rdata); rdata += INT16SZ;
519 rdata += n; /*%< sig */
520 n = ns_get16(rdata); rdata += INT16SZ; /*%< original id */
521 sprintf(buf, "%d", ns_get16(rdata));
522 rdata += INT16SZ;
523 addlen(strlen(buf), &buf, &buflen);
524 break;
525 }
526
527 case ns_t_a6: {
528 struct in6_addr a;
529 int pbyte, pbit;
530
531 /* prefix length */
532 if (rdlen == 0U) goto formerr;
533 len = SPRINTF((tmp, "%d ", *rdata));
534 T(addstr(tmp, len, &buf, &buflen));
535 pbit = *rdata;
536 if (pbit > 128) goto formerr;
537 pbyte = (pbit & ~7) / 8;
538 rdata++;
539
540 /* address suffix: provided only when prefix len != 128 */
541 if (pbit < 128) {
542 if (rdata + pbyte >= edata) goto formerr;
543 memset(&a, 0, sizeof(a));
544 memcpy(&a.s6_addr[pbyte], rdata, sizeof(a) - pbyte);
545 (void) inet_ntop(AF_INET6, &a, buf, buflen);
546 addlen(strlen(buf), &buf, &buflen);
547 rdata += sizeof(a) - pbyte;
548 }
549
550 /* prefix name: provided only when prefix len > 0 */
551 if (pbit == 0)
552 break;
553 if (rdata >= edata) goto formerr;
554 T(addstr(" ", 1, &buf, &buflen));
555 T(addname(msg, msglen, &rdata, origin, &buf, &buflen));
556
557 break;
558 }
559
560 case ns_t_opt: {
561 len = SPRINTF((tmp, "%u bytes", class));
562 T(addstr(tmp, len, &buf, &buflen));
563 break;
564 }
565
566 default:
567 snprintf (errbuf, sizeof (errbuf), "unknown RR type %d", type);
568 comment = errbuf;
569 goto hexify;
570 }
571 return (buf - obuf);
572 formerr:
573 comment = "RR format error";
574 hexify: {
575 int n, m;
576 char *p;
577
578 len = SPRINTF((tmp, "\\# %u%s\t; %s", (unsigned)(edata - rdata),
579 rdlen != 0U ? " (" : "", comment));
580 T(addstr(tmp, len, &buf, &buflen));
581 while (rdata < edata) {
582 p = tmp;
583 p += SPRINTF((p, "\n\t"));
584 spaced = 0;
585 n = MIN(16, edata - rdata);
586 for (m = 0; m < n; m++)
587 p += SPRINTF((p, "%02x ", rdata[m]));
588 T(addstr(tmp, p - tmp, &buf, &buflen));
589 if (n < 16) {
590 T(addstr(")", 1, &buf, &buflen));
591 T(addtab(p - tmp + 1, 48, spaced, &buf, &buflen));
592 }
593 p = tmp;
594 p += SPRINTF((p, "; "));
595 for (m = 0; m < n; m++)
596 *p++ = (isascii(rdata[m]) && isprint(rdata[m]))
597 ? rdata[m]
598 : '.';
599 T(addstr(tmp, p - tmp, &buf, &buflen));
600 rdata += n;
601 }
602 return (buf - obuf);
603 }
604}
605libresolv_hidden_def (ns_sprintrrf)
606
607/* Private. */
608
609/*%
610 * size_t
611 * prune_origin(name, origin)
612 * Find out if the name is at or under the current origin.
613 * return:
614 * Number of characters in name before start of origin,
615 * or length of name if origin does not match.
616 * notes:
617 * This function should share code with samedomain().
618 */
619static size_t
620prune_origin(const char *name, const char *origin) {
621 const char *oname = name;
622
623 while (*name != '\0') {
624 if (origin != NULL && ns_samename(name, origin) == 1)
625 return (name - oname - (name > oname));
626 while (*name != '\0') {
627 if (*name == '\\') {
628 name++;
629 /* XXX need to handle \nnn form. */
630 if (*name == '\0')
631 break;
632 } else if (*name == '.') {
633 name++;
634 break;
635 }
636 name++;
637 }
638 }
639 return (name - oname);
640}
641
642/*%
643 * int
644 * charstr(rdata, edata, buf, buflen)
645 * Format a <character-string> into the presentation buffer.
646 * return:
647 * Number of rdata octets consumed
648 * 0 for protocol format error
649 * -1 for output buffer error
650 * side effects:
651 * buffer is advanced on success.
652 */
653static int
654charstr(const u_char *rdata, const u_char *edata, char **buf, size_t *buflen) {
655 const u_char *odata = rdata;
656 size_t save_buflen = *buflen;
657 char *save_buf = *buf;
658
659 if (addstr("\"", 1, buf, buflen) < 0)
660 goto enospc;
661 if (rdata < edata) {
662 int n = *rdata;
663
664 if (rdata + 1 + n <= edata) {
665 rdata++;
666 while (n-- > 0) {
667 if (strchr("\n\"\\", *rdata) != NULL)
668 if (addstr("\\", 1, buf, buflen) < 0)
669 goto enospc;
670 if (addstr((const char *)rdata, 1,
671 buf, buflen) < 0)
672 goto enospc;
673 rdata++;
674 }
675 }
676 }
677 if (addstr("\"", 1, buf, buflen) < 0)
678 goto enospc;
679 return (rdata - odata);
680 enospc:
681 __set_errno (ENOSPC);
682 *buf = save_buf;
683 *buflen = save_buflen;
684 return (-1);
685}
686
687static int
688addname(const u_char *msg, size_t msglen,
689 const u_char **pp, const char *origin,
690 char **buf, size_t *buflen)
691{
692 size_t newlen, save_buflen = *buflen;
693 char *save_buf = *buf;
694 int n;
695
696 n = dn_expand(msg, msg + msglen, *pp, *buf, *buflen);
697 if (n < 0)
698 goto enospc; /*%< Guess. */
699 newlen = prune_origin(*buf, origin);
700 if (**buf == '\0') {
701 goto root;
702 } else if (newlen == 0U) {
703 /* Use "@" instead of name. */
704 if (newlen + 2 > *buflen)
705 goto enospc; /* No room for "@\0". */
706 (*buf)[newlen++] = '@';
707 (*buf)[newlen] = '\0';
708 } else {
709 if (((origin == NULL || origin[0] == '\0') ||
710 (origin[0] != '.' && origin[1] != '\0' &&
711 (*buf)[newlen] == '\0')) && (*buf)[newlen - 1] != '.') {
712 /* No trailing dot. */
713 root:
714 if (newlen + 2 > *buflen)
715 goto enospc; /* No room for ".\0". */
716 (*buf)[newlen++] = '.';
717 (*buf)[newlen] = '\0';
718 }
719 }
720 *pp += n;
721 addlen(newlen, buf, buflen);
722 **buf = '\0';
723 return (newlen);
724 enospc:
725 __set_errno (ENOSPC);
726 *buf = save_buf;
727 *buflen = save_buflen;
728 return (-1);
729}
730
731static void
732addlen(size_t len, char **buf, size_t *buflen) {
733 assert(len <= *buflen);
734 *buf += len;
735 *buflen -= len;
736}
737
738static int
739addstr(const char *src, size_t len, char **buf, size_t *buflen) {
740 if (len >= *buflen) {
741 __set_errno (ENOSPC);
742 return (-1);
743 }
744 memcpy(*buf, src, len);
745 addlen(len, buf, buflen);
746 **buf = '\0';
747 return (0);
748}
749
750static int
751addtab(size_t len, size_t target, int spaced, char **buf, size_t *buflen) {
752 size_t save_buflen = *buflen;
753 char *save_buf = *buf;
754 int t;
755
756 if (spaced || len >= target - 1) {
757 T(addstr(" ", 2, buf, buflen));
758 spaced = 1;
759 } else {
760 for (t = (target - len - 1) / 8; t >= 0; t--)
761 if (addstr("\t", 1, buf, buflen) < 0) {
762 *buflen = save_buflen;
763 *buf = save_buf;
764 return (-1);
765 }
766 spaced = 0;
767 }
768 return (spaced);
769}
770