/*
 * Taken from a curses distribution.
 */
/*
 * _tracef(format, args)
 *
 * Writes tracing information to file "trace.out" if tracing is
 * enabled.  Formatting follows printf conventions with some
 * minor differences:
 *	%b		unsigned binary integer
 *	%c		character, output as 'x'
 *	%d		signed decimal integer
 *	%o		unsigned octal integer
 *	%s		string (terminated by EOS), output as "xxx"
 *	%*s		counted string (takes two args), output as "xxx"
 *	%x		unsigned hex integer
 * Field and width parameters (as in printf) aren't permitted.
 * traceon()		turns on tracing
 * traceoff()		turns off tracing
 *
 * Note: #ifdef decus stuff hasn't been tested.
 */

#include	<stdio.h>
#define	FALSE	0
#define	TRUE	1
#define	EOS	'\0'

#ifdef	decus
static FILE	tracefd;
#else
static int	tracefd;
#endif

#ifdef vms
#include		<ssdef.h>
#include		<stsdef.h>
#define	IO_SUCCESS	(SS$_NORMAL | STS$M_INHIB_MSG)
#define	IO_ERROR	SS$_ABORT
#endif
/*
 * Note: IO_SUCCESS and IO_ERROR are defined in the Decus C stdio.h file
 */
#ifndef	IO_SUCCESS
#define	IO_SUCCESS	0
#endif
#ifndef	IO_ERROR
#define	IO_ERROR	1
#endif

static int	tracing = FALSE;
static int	file_opened = FALSE;

traceon()
/*
 * Turn on tracing, opening the trace file if necessary.
 */
{
	if (!file_opened) {
	    file_opened = TRUE;
#ifdef	decus
	    if ((tracefd = fopen("trace.out", "w")) == NULL) {
		perror("trace.out");
		error("trace: can't create trace.out\n");
	    }
#else
	    if ((tracefd = creat("trace.out", 0644)) < 0) {
		perror("trace.out");
		exit(IO_ERROR);
	    }
#endif
	}
	_tracef("traceon() called");
    	tracing = TRUE;
}

traceoff()
{
	_tracef("traceoff() called");
    	tracing = FALSE;
}

/*
 * XBUFSIZE is the length of a printed line in the trace output file.
 */
#define	XBUFSIZE	80
static char	xbuffer[XBUFSIZE + 2];
static char	*xbufp;

_tracef(fmt, args)
char	*fmt;
int	args;
{
    	int		*parm;
	char		buffer[512];
	auto char	*bufp;
	char		*bufe;
	char		bytebuff[512];	/* Bytes stored here for output	*/
	char		*bytep;		/* Pointer into bytebuff[]	*/
	int		stringcount;	/* Count for %*s format		*/
	char		*stringp;	/* String pointer for %*s	*/

	parm = &args;
	bufp = buffer;
	if (!tracing)
	    return;
	while (*fmt) {
	    if (*fmt == '%') {
		fmt++;
		switch (*fmt) {
		case 'b':
		    addnum(&bufp, *(parm++), 2);
		    break;

		case 'd':
		    addnum(&bufp, *(parm++), 10);
		    break;

		case 'o':
		    addnum(&bufp, *(parm++), 8);
		    break;

		case 'x':
		    addnum(&bufp, *(parm++), 16);
		    break;

		case 'c':
		    *(bufp++) = '\'';
		    *(bufp++) = *(parm++);
		    *(bufp++) = '\'';
		    break;

		case '*':			/* Counted string	*/
		    if (fmt[1] != 's')
			break;
		    fmt++;
		    stringcount = *(parm++);
		    *(bufp++) = '"';
		    stringp = *(parm++);
		    if (stringp == NULL) {
			strcpy(bufp, "?NULL?");
			bufp += 6;
		    }
		    else while (--stringcount) {
			*bufp++ = *stringp++;
		    }
		    *bufp++ = '"';
		    break;

		case 's':
		    if (*parm) {
			*(bufp++) = '"';
			strcpy(bufp, *parm);
			bufp += strlen(*parm);
			*(bufp++) = '"';
		    }
		    else {
			 strcpy(bufp, "NULL");
			 bufp += 4;
		    }
		    parm++;
		    break;
		}
	    }
	    else {
		*(bufp++) = *fmt;
	    }
	    fmt++;
	}
	*(bufp++) = '\n';
	/*
 	 * Write the buffer in datascope mode.
	 */
	bufe = bufp;
	xbufp = xbuffer;
	for (bufp = buffer; bufp < bufe; bufp++) {
	    bytep = bytebuff;
	    *bufp &= 0377;
	    if (*bufp == '\n')
		_traceout("\n");
	    else if (*bufp == '\033')
		_traceout("<ESC>");
	    else {
		if (*bufp >= 0177) {
		    *bytep++ = '~';
		    *bufp -= 0200;
		}
		if (*bufp < ' ' && *bufp != '\t') {
		    *bytep++ = '^';
		    *bytep++ = (*bufp + '@');
		}
		else *bytep++ = *bufp;
		*bytep = '\0';
		_traceout(bytebuff);
	    }
	}
	_traceout(NULL);
}

#define	GUARD	4

static
_traceout(string)
char		*string;
/*
 * Output routine for _tracef() --
 *	"\n"	End of line from user.
 *	NULL	end of text.  Dump current buffer if non-empty
 *	string	Stuff it if there's room, else get a new line.
 */
{
	if (string == NULL) {
	    if (xbufp > xbuffer) {
#ifdef decus
		fprintf(tracefd, "%.*s", xbufp - xbuffer, xbuffer);
#else
		write(tracefd, xbuffer, xbufp - xbuffer);
#endif
		xbufp = xbuffer;
	    }
	}
	else if (string[0] == '\n' && string[1] == '\0') {
	    *xbufp++ = '\n';
#ifdef decus
		fprintf(tracefd, "%.*s", xbufp - xbuffer, xbuffer);
#else
		write(tracefd, xbuffer, xbufp - xbuffer);
#endif
	    xbufp = xbuffer;
	}
	else if (xbufp + strlen(string) < &xbuffer[XBUFSIZE - GUARD]) {
	    while (*string != '\0') {
		*xbufp++ = *string++;
	    }
	}
	else {
	    while (xbufp < &xbuffer[XBUFSIZE - GUARD])
		*xbufp++ = ' ';
	    strcpy(xbufp, "|<--\n");
#ifdef decus
	    fprintf(tracefd, "%s", xbuffer);
#else
	    write(tracefd, xbuffer, strlen(xbuffer));
#endif
	    strcpy(xbuffer, string);
	    xbufp = &xbuffer[strlen(string)];
	}
}

static
addnum(bufp, num, base)
char		**bufp;			/* Output pointer		*/
int		num;			/* To be converted		*/
int		base;			/* Output base			*/
/*
 * Convert the number to ascii.  Note: base 10 is signed, others are
 * unsigned.
 */
{
	register unsigned int	temp;

    	if (base == 10 && num < 0) {
	    num = -num;
	    *((*bufp)++) = '-';
	    if (num < 0) {
		/*
		 * The dreaded 32768 -- the following will not work
		 * everywhere, but will do for now.
		 */
#if (0x8000 < 0)
		*((*bufp)++) = '3';	/* 16 bit twos-complement	*/
		num = 2768;
#else
		*((*bufp)++) = '2';	/* 32 bit twos-complement	*/
		num = 147483648;
#endif
	    }
	}
	if ((temp = ((unsigned) num) / base) != 0)
	    addnum(bufp, temp, base);
	*((*bufp)++) = "0123456789abcdef"[((unsigned) num) % base];
}
