:i	static char d_dsp_sccs_id[] = "%W% %G%";
:1#include "V.h"
/*
* This module contains a few functions that are usually called via the macros:
*   Dsp(c)
*   Dspa(s,n)
*   Dsps(s,n)
*   Dspx(s,n)
*
* These take a char c or an n-byte string s and  return  a  printable  string
* that represents their value. Dsp(c) is suitable for feeding to a %c format;
* the output of the others are suitable for the %s format.  If n<0, strlen(s)
* will  be  used,  so  the  caller  need not know the length.  The difference
* between the three string routines is:  Dspa(s,n) converts  s  to  an  ASCII
* representation of the form used in "quoted" C strings, with NL converted to
* \n, etc.  Dspx(s,n) converts s to hex, with spaces  between  each  two  hex
* digits.   Dsps(s,n)  tries  to  determine  whether  the string looks like a
* printable ASCII string; if so, it is like Dspa(s,n) except that it  has  an
* initial  and  final  double  quote; if not, Dspx is invoked to return a hex
* string.
*/

/*
* We have a set of NBUFS circular buffers so that we can be called up to NBUFS
* times in a single printf() without stepping on our own feet.
*/
#define NBUFS 100	/* Number of buffers */
#define SBUFS 510	/* Size of buffers */
static char* buf[NBUFS] = {0};
global int   dspxword = 4;	/* Word size for dspx() */
static int   dspquote = 0;	/* If nonnull, quotes the string */
static int   which = 0;

/*
* Return a "display" version of a byte, i.e., a printable ASCII char. You may
* wish to modify this somewhat if your system is 8-bit clean.
*/
dsp(c)
{
	c &= 0x7F;
	if (' '<=c && c<=0x7E)
		return c;
	return '_';
}

/*
* Return a "display" version of a string, after first trying to intuit whether
* we  have a printable string or not.  If not, we invoke dpsx to produce a hex
* string; if yes, we use dspa, which will turn white space other than ' ' into
* escape  sequences.   An  added benefit of using this routine is that it puts
* quotes around the ASCII (but not the hex) string.
*/
char * dsps(s,n)
	byte* s;
	int   n;
{	char* r=0;
	int   c, i;
:f	Fenter("dsps");

	if (!s) s = (BP)m_null;
	if (n < 0) n = strlen((CP)s);
/*	if (n < 1) {r = null;Done;} */
	for (i=0; i<n; i++) {	/* Look for non-printable, non-white chars */
		if ((c = s[i]) && !(isprint(c) || isspace(c))) {
			r = dspx(s,n);	/* If we find one, return a hex string */
			Done;
		}
	}
	dspquote = '"';	/* Quote that sucker, he said */
	r = dspa(s,n);
fail:
done:
	dspquote = 0;		/* Unquote */
:f	Fexit;
	return r;
}

/*
* Return a "display" version of a string, i.e., a string printable char.  Note
* that we accept n<0 as meaning to call strlen(s) to get the length.
*/
char * dspa(s,n)
	byte* s;
	int   n;
{	int   c;
	char* b;
	char* z;
:f	Fenter("dspa");
	which = (which + 1) % NBUFS;
	if (!buf[which]) {
		if (!(buf[which] = (CP)MallocM(SBUFS+2,"dsp-buf")))
			Fail;
:8		V8 "buf[%d]=%08X",which,buf[which] D;
	}
	b = buf[which];
	z = b + SBUFS - 2;
	if ( !s) s = (BP)m_null;
	if (n<0) n = strlen((CP)s);
	if (dspquote) *b++ = dspquote;
	while (n-- > 0 && b < z) {
		c = *s++;
		c = B7(c);
		if (c < 0x20) {
			if (c == dspquote) {	/* Quotes are escaped */
				*b++ = '\\'; *b++ = c;
			} else
			Switch(c) {
			  case '\b': *b++ = '\\'; *b++ = 'b'; break;
			  case '\f': *b++ = '\\'; *b++ = 'f'; break;
			  case '\n': *b++ = '\\'; *b++ = 'n'; break;
			  case '\r': *b++ = '\\'; *b++ = 'r'; break;
			  case '\t': *b++ = '\\'; *b++ = 't'; break;
			  case '\0': *b++ = '\\'; *b++ = '0'; break;
			  case '\\': *b++ = '\\'; *b++ = '\\'; break;
			  default  : *b++ = '^' ; *b++ = 'A' - 1 + c;
			}
		} else
		if (c == 0x7F) {
			*b++ = '\\'; *b++ = 'D';
		} else {
			*b++ = c;
		}
	}
	if (dspquote) *b++ = dspquote;
	*b = 0;
fail:
:f	Fexit;
	return buf[which];
}

/*
* Return a "hex" version of a string, i.e., a string printable char.  This one
* also accepts n<0 as meaning to call strlen(s) to get the length.
*/
char * dspx(s,n)
	byte* s;
	int   n;
{	int   c, i;
	char* b;
	char* z;
:f	Fenter("dspx");
	if ( !s) {s = (BP)null; n = 0;}
	if (n<0) n = strlen((CP)s);
	which = (which + 1) % NBUFS;
	if (!buf[which]) {
		if (!(buf[which] = (CP)MallocM(SBUFS+2,Fctname)))
			Fail;
:8		V8 "buf[%d]=%08X",which,buf[which] D;
	}
	b = buf[which];
	z = b + SBUFS - 3;
	for (i=0; i<n && b<z; i++) {
		c = s[i];
		if (dspxword && i && !(i % dspxword))
			*b++ = ' ';
		*b++ = hex_dig[c>>4];
		*b++ = hex_dig[c&15];
	}
	*b = 0;
fail:
:f	Fexit;
	return buf[which];
}
