1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <time.h>
36 #include <sys/time.h>
37 #include <sys/bufmod.h>
38 #include <setjmp.h>
39 #include <stdarg.h>
40 #include <sys/socket.h>
41 #include <net/if.h>
42 #include <netinet/in_systm.h>
43 #include <netinet/in.h>
44 #include <netinet/ip.h>
45 #include <netinet/if_ether.h>
46 #include <rpc/types.h>
47 #include <rpc/xdr.h>
48 #include <inttypes.h>
49 
50 #include "snoop.h"
51 
52 char *dlc_header;
53 char *src_name, *dst_name;
54 int pi_frame;
55 int pi_time_hour;
56 int pi_time_min;
57 int pi_time_sec;
58 int pi_time_usec;
59 
60 #ifndef MIN
61 #define	MIN(a, b) ((a) < (b) ? (a) : (b))
62 #endif
63 
64 static void hexdump(char *, int);
65 
66 /*
67  * This routine invokes the packet interpreters
68  * on a packet.  There's some messing around
69  * setting up a few packet-externals before
70  * starting with the ethernet interpreter.
71  * Yes, we assume here that all packets will
72  * be ethernet packets.
73  */
74 void
process_pkt(struct sb_hdr * hdrp,char * pktp,int num,int flags)75 process_pkt(struct sb_hdr *hdrp, char *pktp, int num, int flags)
76 {
77 	int drops, pktlen;
78 	struct timeval *tvp;
79 	struct tm *tm;
80 	extern int x_offset;
81 	extern int x_length;
82 	int offset, length;
83 	static struct timeval ptv;
84 
85 	if (hdrp == NULL)
86 		return;
87 
88 	tvp = &hdrp->sbh_timestamp;
89 	if (ptv.tv_sec == 0)
90 		ptv = *tvp;
91 	drops  = hdrp->sbh_drops;
92 	pktlen = hdrp->sbh_msglen;
93 	if (pktlen <= 0)
94 		return;
95 
96 	/* set up externals */
97 	dlc_header = pktp;
98 	pi_frame = num;
99 	tm = localtime(&tvp->tv_sec);
100 	pi_time_hour = tm->tm_hour;
101 	pi_time_min  = tm->tm_min;
102 	pi_time_sec  = tm->tm_sec;
103 	pi_time_usec = tvp->tv_usec;
104 
105 	src_name = "?";
106 	dst_name = "*";
107 
108 	click(hdrp->sbh_origlen);
109 
110 	(*interface->interpreter)(flags, dlc_header, hdrp->sbh_msglen,
111 	    hdrp->sbh_origlen);
112 
113 	show_pktinfo(flags, num, src_name, dst_name, &ptv, tvp, drops,
114 	    hdrp->sbh_origlen);
115 
116 	if (x_offset >= 0) {
117 		offset = MIN(x_offset, hdrp->sbh_msglen);
118 		offset -= (offset % 2);  /* round down */
119 		length = MIN(hdrp->sbh_msglen - offset, x_length);
120 
121 		hexdump(dlc_header + offset, length);
122 	}
123 
124 	ptv = *tvp;
125 }
126 
127 
128 /*
129  * *************************************************************
130  * The following routines constitute a library
131  * used by the packet interpreters to facilitate
132  * the display of packet data.  This library
133  * of routines helps provide a consistent
134  * "look and feel".
135  */
136 
137 
138 /*
139  * Display the value of a flag bit in
140  * a byte together with some text that
141  * corresponds to its value - whether
142  * true or false.
143  */
144 char *
getflag(int val,int mask,char * s_true,char * s_false)145 getflag(int val, int mask, char *s_true, char *s_false)
146 {
147 	static char buff[80];
148 	char *p;
149 	int set;
150 
151 	(void) strcpy(buff, ".... .... = ");
152 	if (s_false == NULL)
153 		s_false = s_true;
154 
155 	for (p = &buff[8]; p >= buff; p--) {
156 		if (*p == ' ')
157 			p--;
158 		if (mask & 0x1) {
159 			set = val & mask & 0x1;
160 			*p = set ? '1':'0';
161 			(void) strcat(buff, set ? s_true: s_false);
162 			break;
163 		}
164 		mask >>= 1;
165 		val  >>= 1;
166 	}
167 	return (buff);
168 }
169 
170 XDR xdrm;
171 jmp_buf xdr_err;
172 int xdr_totlen;
173 char *prot_prefix;
174 char *prot_nest_prefix = "";
175 char *prot_title;
176 
177 void
show_header(char * pref,char * str,int len)178 show_header(char *pref, char *str, int len)
179 {
180 	prot_prefix = pref;
181 	prot_title = str;
182 	(void) sprintf(get_detail_line(0, len), "%s%s----- %s -----",
183 	    prot_nest_prefix, pref, str);
184 }
185 
186 void
xdr_init(char * addr,int len)187 xdr_init(char *addr, int len)
188 {
189 	xdr_totlen = len;
190 	xdrmem_create(&xdrm, addr, len, XDR_DECODE);
191 }
192 
193 /* Note: begin+end are ignored in get_detail_line */
194 char *
get_line(int begin,int end)195 get_line(int begin, int end)
196 {
197 	char *line;
198 
199 	line = get_detail_line(begin, end);
200 	(void) strcpy(line, prot_nest_prefix);
201 	(void) strcat(line, prot_prefix);
202 	return (line + strlen(line));
203 }
204 
205 int
get_line_remain(void)206 get_line_remain(void)
207 {
208 	return (MAXLINE - strlen(prot_nest_prefix) - strlen(prot_prefix));
209 }
210 
211 void
show_line(char * str)212 show_line(char *str)
213 {
214 	(void) strlcpy(get_line(0, 0), str, get_line_remain());
215 }
216 
217 void
show_printf(char * fmt,...)218 show_printf(char *fmt, ...)
219 {
220 	va_list ap;
221 
222 	va_start(ap, fmt);
223 	(void) vsnprintf(get_line(0, 0), get_line_remain(), fmt, ap);
224 	va_end(ap);
225 }
226 
227 char
getxdr_char()228 getxdr_char()
229 {
230 	char s;
231 
232 	if (xdr_char(&xdrm, &s))
233 		return (s);
234 	longjmp(xdr_err, 1);
235 	/* NOTREACHED */
236 }
237 
238 char
showxdr_char(char * fmt)239 showxdr_char(char *fmt)
240 {
241 	int pos; char val;
242 
243 	pos = getxdr_pos();
244 	val = getxdr_char();
245 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
246 	return (val);
247 }
248 
249 uchar_t
getxdr_u_char()250 getxdr_u_char()
251 {
252 	uchar_t s;
253 
254 	if (xdr_u_char(&xdrm, &s))
255 		return (s);
256 	longjmp(xdr_err, 1);
257 	/* NOTREACHED */
258 }
259 
260 uchar_t
showxdr_u_char(char * fmt)261 showxdr_u_char(char *fmt)
262 {
263 	int pos;
264 	uchar_t val;
265 
266 	pos = getxdr_pos();
267 	val = getxdr_u_char();
268 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
269 	return (val);
270 }
271 
272 short
getxdr_short()273 getxdr_short()
274 {
275 	short s;
276 
277 	if (xdr_short(&xdrm, &s))
278 		return (s);
279 	longjmp(xdr_err, 1);
280 	/* NOTREACHED */
281 }
282 
283 short
showxdr_short(char * fmt)284 showxdr_short(char *fmt)
285 {
286 	int pos; short val;
287 
288 	pos = getxdr_pos();
289 	val = getxdr_short();
290 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
291 	return (val);
292 }
293 
294 ushort_t
getxdr_u_short()295 getxdr_u_short()
296 {
297 	ushort_t s;
298 
299 	if (xdr_u_short(&xdrm, &s))
300 		return (s);
301 	longjmp(xdr_err, 1);
302 	/* NOTREACHED */
303 }
304 
305 ushort_t
showxdr_u_short(char * fmt)306 showxdr_u_short(char *fmt)
307 {
308 	int pos;
309 	ushort_t val;
310 
311 	pos = getxdr_pos();
312 	val = getxdr_u_short();
313 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
314 	return (val);
315 }
316 
317 long
getxdr_long()318 getxdr_long()
319 {
320 	long l;
321 
322 	if (xdr_long(&xdrm, &l))
323 		return (l);
324 	longjmp(xdr_err, 1);
325 	/* NOTREACHED */
326 }
327 
328 long
showxdr_long(char * fmt)329 showxdr_long(char *fmt)
330 {
331 	int pos; long val;
332 
333 	pos = getxdr_pos();
334 	val = getxdr_long();
335 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
336 	return (val);
337 }
338 
339 ulong_t
getxdr_u_long()340 getxdr_u_long()
341 {
342 	ulong_t l;
343 
344 	if (xdr_u_long(&xdrm, &l))
345 		return (l);
346 	longjmp(xdr_err, 1);
347 	/* NOTREACHED */
348 }
349 
350 ulong_t
showxdr_u_long(char * fmt)351 showxdr_u_long(char *fmt)
352 {
353 	int pos;
354 	ulong_t val;
355 
356 	pos = getxdr_pos();
357 	val = getxdr_u_long();
358 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
359 	return (val);
360 }
361 
362 longlong_t
getxdr_longlong()363 getxdr_longlong()
364 {
365 	longlong_t l;
366 
367 	if (xdr_longlong_t(&xdrm, &l))
368 		return (l);
369 	longjmp(xdr_err, 1);
370 	/* NOTREACHED */
371 }
372 
373 longlong_t
showxdr_longlong(char * fmt)374 showxdr_longlong(char *fmt)
375 {
376 	int pos; longlong_t val;
377 
378 	pos = getxdr_pos();
379 	val = getxdr_longlong();
380 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
381 	return (val);
382 }
383 
384 u_longlong_t
getxdr_u_longlong()385 getxdr_u_longlong()
386 {
387 	u_longlong_t l;
388 
389 	if (xdr_u_longlong_t(&xdrm, &l))
390 		return (l);
391 	longjmp(xdr_err, 1);
392 	/* NOTREACHED */
393 }
394 
395 u_longlong_t
showxdr_u_longlong(char * fmt)396 showxdr_u_longlong(char *fmt)
397 {
398 	int pos; u_longlong_t val;
399 
400 	pos = getxdr_pos();
401 	val = getxdr_u_longlong();
402 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, val);
403 	return (val);
404 }
405 
406 bool_t
getxdr_bool()407 getxdr_bool()
408 {
409 	bool_t b;
410 
411 	if (xdr_bool(&xdrm, &b))
412 		return (b);
413 	longjmp(xdr_err, 1);
414 	/* NOTREACHED */
415 }
416 
417 bool_t
showxdr_bool(char * fmt)418 showxdr_bool(char *fmt)
419 {
420 	int pos; bool_t val;
421 
422 	pos = getxdr_pos();
423 	val = getxdr_bool();
424 	(void) sprintf(get_line(pos, getxdr_pos()), fmt,
425 	    val ? "True" : "False");
426 	return (val);
427 }
428 
429 char *
getxdr_opaque(char * p,int len)430 getxdr_opaque(char *p, int len)
431 {
432 	if (xdr_opaque(&xdrm, p, len))
433 		return (p);
434 	longjmp(xdr_err, 1);
435 	/* NOTREACHED */
436 }
437 
438 char *
getxdr_string(char * p,int len)439 getxdr_string(char *p, /* len+1 bytes or longer */
440 	int len)
441 {
442 	if (xdr_string(&xdrm, &p, len))
443 		return (p);
444 	longjmp(xdr_err, 1);
445 	/* NOTREACHED */
446 }
447 
448 char *
showxdr_string(int len,char * fmt)449 showxdr_string(int len, /* XDR length */
450 	char *fmt)
451 {
452 	static int buff_len = 0;
453 	static char *buff = NULL;
454 	int pos;
455 
456 	/*
457 	 * XDR strings don't necessarily have a trailing null over the
458 	 * wire.  However, the XDR code will put one in for us.  Make sure
459 	 * we have allocated room for it.
460 	 */
461 	len++;
462 
463 	if ((len > buff_len) || (buff_len == 0)) {
464 		if (buff)
465 			free(buff);
466 		if ((buff = (char *)malloc(len)) == NULL)
467 			pr_err("showxdr_string: no mem");
468 		buff_len = len;
469 	}
470 	pos = getxdr_pos();
471 	getxdr_string(buff, len);
472 	(void) strcpy(buff+60, "...");
473 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, buff);
474 	return (buff);
475 }
476 
477 char *
getxdr_bytes(uint_t * lenp)478 getxdr_bytes(uint_t *lenp)
479 {
480 	static char buff[1024];
481 	char *p = buff;
482 
483 	if (xdr_bytes(&xdrm, &p, lenp, 1024))
484 		return (buff);
485 	longjmp(xdr_err, 1);
486 	/* NOTREACHED */
487 }
488 
489 char *
getxdr_context(char * p,int len)490 getxdr_context(char *p, int len)
491 {
492 	ushort_t size;
493 
494 	size = getxdr_u_short();
495 	if (((int)size > 0) && ((int)size < len) && getxdr_opaque(p, size))
496 		return (p);
497 	longjmp(xdr_err, 1);
498 	/* NOTREACHED */
499 }
500 
501 char *
showxdr_context(char * fmt)502 showxdr_context(char *fmt)
503 {
504 	ushort_t size;
505 	static char buff[1024];
506 	int pos;
507 
508 	pos = getxdr_pos();
509 	size = getxdr_u_short();
510 	if (((int)size > 0) && ((int)size < 1024) &&
511 	    getxdr_opaque(buff, size)) {
512 		(void) sprintf(get_line(pos, getxdr_pos()), fmt, buff);
513 		return (buff);
514 	}
515 	longjmp(xdr_err, 1);
516 	/* NOTREACHED */
517 }
518 
519 enum_t
getxdr_enum()520 getxdr_enum()
521 {
522 	enum_t e;
523 
524 	if (xdr_enum(&xdrm, &e))
525 		return (e);
526 	longjmp(xdr_err, 1);
527 	/* NOTREACHED */
528 }
529 
530 void
xdr_skip(int delta)531 xdr_skip(int delta)
532 {
533 	uint_t pos;
534 	if (delta % 4 != 0 || delta < 0)
535 		longjmp(xdr_err, 1);
536 	/* Check for overflow */
537 	pos = xdr_getpos(&xdrm);
538 	if ((pos + delta) < pos)
539 		longjmp(xdr_err, 1);
540 	/* xdr_setpos() checks for buffer overrun */
541 	if (xdr_setpos(&xdrm, pos + delta) == FALSE)
542 		longjmp(xdr_err, 1);
543 }
544 
545 int
getxdr_pos()546 getxdr_pos()
547 {
548 	return (xdr_getpos(&xdrm));
549 }
550 
551 void
setxdr_pos(int pos)552 setxdr_pos(int pos)
553 {
554 	xdr_setpos(&xdrm, pos);
555 }
556 
557 void
show_space()558 show_space()
559 {
560 	(void) get_line(0, 0);
561 }
562 
563 void
show_trailer()564 show_trailer()
565 {
566 	show_space();
567 }
568 
569 char *
getxdr_date()570 getxdr_date()
571 {
572 	time_t sec;
573 	int  usec;
574 	static char buff[64];
575 	char *p;
576 	struct tm my_time;	/* private buffer to avoid collision */
577 				/* between gmtime and strftime */
578 	struct tm *tmp;
579 
580 	sec  = getxdr_long();
581 	usec = getxdr_long();
582 	if (sec == -1)
583 		return ("-1 ");
584 
585 	if (sec < 3600 * 24 * 365) {	/* assume not a date */
586 		(void) sprintf(buff, "%d.%06d", sec, usec);
587 	} else {
588 		tmp = gmtime(&sec);
589 		(void) memcpy(&my_time, tmp, sizeof (struct tm));
590 		strftime(buff, sizeof (buff), "%d-%h-%y %T.", &my_time);
591 		p = buff + strlen(buff);
592 		(void) sprintf(p, "%06d GMT", usec);
593 	}
594 	return (buff);
595 }
596 
597 char *
showxdr_date(char * fmt)598 showxdr_date(char *fmt)
599 {
600 	int pos;
601 	char *p;
602 
603 	pos = getxdr_pos();
604 	p = getxdr_date();
605 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
606 	return (p);
607 }
608 
609 char *
getxdr_date_ns(void)610 getxdr_date_ns(void)
611 {
612 	time_t sec, nsec;
613 
614 	sec  = getxdr_long();
615 	nsec = getxdr_long();
616 	if (sec == -1)
617 		return ("-1 ");
618 	else
619 		return (format_time(sec, nsec));
620 }
621 
622 /*
623  * Format the given time.
624  */
625 char *
format_time(int64_t sec,uint32_t nsec)626 format_time(int64_t sec, uint32_t nsec)
627 {
628 	static char buff[64];
629 	char *p;
630 	struct tm my_time;	/* private buffer to avoid collision */
631 				/* between gmtime and strftime */
632 	struct tm *tmp;
633 
634 	if (sec < 3600 * 24 * 365) {
635 		/* assume not a date; includes negative times */
636 		(void) sprintf(buff, "%lld.%06d", sec, nsec);
637 	} else if (sec > INT32_MAX) {
638 		/*
639 		 * XXX No routines are available yet for formatting 64-bit
640 		 * times.
641 		 */
642 		(void) sprintf(buff, "%lld.%06d", sec, nsec);
643 	} else {
644 		time_t sec32 = (time_t)sec;
645 
646 		tmp = gmtime(&sec32);
647 		memcpy(&my_time, tmp, sizeof (struct tm));
648 		strftime(buff, sizeof (buff), "%d-%h-%y %T.", &my_time);
649 		p = buff + strlen(buff);
650 		(void) sprintf(p, "%09d GMT", nsec);
651 	}
652 	return (buff);
653 }
654 
655 char *
showxdr_date_ns(char * fmt)656 showxdr_date_ns(char *fmt)
657 {
658 	int pos;
659 	char *p;
660 
661 	pos = getxdr_pos();
662 	p = getxdr_date_ns();
663 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
664 	return (p);
665 }
666 
667 char *
getxdr_time()668 getxdr_time()
669 {
670 	time_t sec;
671 	static char buff[64];
672 	struct tm my_time;	/* private buffer to avoid collision */
673 				/* between gmtime and strftime */
674 	struct tm *tmp;
675 
676 	sec  = getxdr_long();
677 	if (sec == -1)
678 		return ("-1 ");
679 
680 	if (sec < 3600 * 24 * 365) {	/* assume not a date */
681 		(void) sprintf(buff, "%d", sec);
682 	} else {
683 		tmp = gmtime(&sec);
684 		memcpy(&my_time, tmp, sizeof (struct tm));
685 		strftime(buff, sizeof (buff), "%d-%h-%y %T", &my_time);
686 	}
687 	return (buff);
688 }
689 
690 char *
showxdr_time(char * fmt)691 showxdr_time(char *fmt)
692 {
693 	int pos;
694 	char *p;
695 
696 	pos = getxdr_pos();
697 	p = getxdr_time();
698 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
699 	return (p);
700 }
701 
702 char *
getxdr_hex(int len)703 getxdr_hex(int len)
704 {
705 	int i, j;
706 	static char hbuff[1024];
707 	char rbuff[1024];
708 	static char *hexstr = "0123456789ABCDEF";
709 	char toobig = 0;
710 
711 	if (len == 0) {
712 		hbuff[0] = '\0';
713 		return (hbuff);
714 	}
715 	if (len > 1024)
716 		len = 1024;
717 	if (len < 0 || xdr_opaque(&xdrm, rbuff, len) == FALSE) {
718 		longjmp(xdr_err, 1);
719 	}
720 
721 	if (len * 2 > sizeof (hbuff)) {
722 		toobig++;
723 		len = sizeof (hbuff) / 2;
724 	}
725 
726 	j = 0;
727 	for (i = 0; i < len; i++) {
728 		hbuff[j++] = hexstr[rbuff[i] >> 4 & 0x0f];
729 		hbuff[j++] = hexstr[rbuff[i] & 0x0f];
730 	}
731 
732 	if (toobig) {
733 		hbuff[len * 2 - strlen("<Too Long>")] = '\0';
734 		strcat(hbuff, "<Too Long>");
735 	} else
736 		hbuff[j] = '\0';
737 
738 	return (hbuff);
739 }
740 
741 char *
showxdr_hex(int len,char * fmt)742 showxdr_hex(int len, char *fmt)
743 {
744 	int pos;
745 	char *p;
746 
747 	pos = getxdr_pos();
748 	p = getxdr_hex(len);
749 	(void) sprintf(get_line(pos, getxdr_pos()), fmt, p);
750 	return (p);
751 }
752 
753 static void
hexdump(char * data,int datalen)754 hexdump(char *data, int datalen)
755 {
756 	char *p;
757 	ushort_t *p16 = (ushort_t *)data;
758 	char *p8 = data;
759 	int i, left, len;
760 	int chunk = 16;  /* 16 bytes per line */
761 
762 	printf("\n");
763 
764 	for (p = data; p < data + datalen; p += chunk) {
765 		printf("\t%4d: ", p - data);
766 		left = (data + datalen) - p;
767 		len = MIN(chunk, left);
768 		for (i = 0; i < (len / 2); i++)
769 			printf("%04x ", ntohs(*p16++) & 0xffff);
770 		if (len % 2) {
771 			printf("%02x   ", *((unsigned char *)p16));
772 		}
773 		for (i = 0; i < (chunk - left) / 2; i++)
774 			printf("     ");
775 
776 		printf("   ");
777 		for (i = 0; i < len; i++, p8++)
778 			printf("%c", isprint(*p8) ? *p8 : '.');
779 		printf("\n");
780 	}
781 
782 	printf("\n");
783 }
784 
785 char *
show_string(const char * str,int dlen,int maxlen)786 show_string(const char *str, int dlen, int maxlen)
787 /*
788  *   Prints len bytes from str enclosed in quotes.
789  *   If len is negative, length is taken from strlen(str).
790  *   No more than maxlen bytes will be printed.  Longer
791  *   strings are flagged with ".." after the closing quote.
792  *   Non-printing characters are converted to C-style escape
793  *   codes or octal digits.
794  */
795 {
796 #define	TBSIZE	256
797 	static char tbuff[TBSIZE];
798 	const char *p;
799 	char *pp;
800 	int printable = 0;
801 	int c, len;
802 
803 	len = dlen > maxlen ? maxlen : dlen;
804 	dlen = len;
805 
806 	for (p = str, pp = tbuff; len; p++, len--) {
807 		switch (c = *p & 0xFF) {
808 		case '\n': (void) strcpy(pp, "\\n"); pp += 2; break;
809 		case '\b': (void) strcpy(pp, "\\b"); pp += 2; break;
810 		case '\t': (void) strcpy(pp, "\\t"); pp += 2; break;
811 		case '\r': (void) strcpy(pp, "\\r"); pp += 2; break;
812 		case '\f': (void) strcpy(pp, "\\f"); pp += 2; break;
813 		default:
814 			if (isascii(c) && isprint(c)) {
815 				*pp++ = c;
816 				printable++;
817 			} else {
818 				(void) snprintf(pp, TBSIZE - (pp - tbuff),
819 					isdigit(*(p + 1)) ?
820 					"\\%03o" : "\\%o", c);
821 				pp += strlen(pp);
822 			}
823 			break;
824 		}
825 		*pp = '\0';
826 		/*
827 		 * Check for overflow of temporary buffer.  Allow for
828 		 * the next character to be a \nnn followed by a trailing
829 		 * null.  If not, then just bail with what we have.
830 		 */
831 		if (pp + 5 >= &tbuff[TBSIZE]) {
832 			break;
833 		}
834 	}
835 	return (printable > dlen / 2 ? tbuff : "");
836 }
837