/* ansify.c - convert text and attributes to ANSI text string
 */

#include <stdio.h>
#include <string.h>
#include "vt100.h"


/* Append at most "*n" bytes of "src" to "dest", and update "*n" to reflect
 * the amount of space left. The string in "dest" is always null terminated.
 *
 * Returns nonzero if the whole string could not be appended; if so, "dest"
 * is reset to its original size and "*n" is restored. This is a bit wasteful
 * as the string is actually transferred but the old terminator is stuck back
 * in when it becomes apparent that there isn't room; perhaps a faster
 * alternative should be coded.
 */
int mystrncat(char *dest, char *src, int *n)
{
	char *ptr;
	int oldn;

	if ((!dest) || (!src) || (!n))
		return (1);

	if (*n <= 1)
		return (1);		    /* need at least 2 bytes, one for terminator */

	while (*dest)
		dest++;			    /* find the end of "dest" */

	oldn = *n;
	ptr = dest;

	while (((*n) > 1) && (*src)) {
		*dest = *src;
		dest++;
		src++;
		*dest = 0;
		(*n)--;
	}

	if (*src == 0)
		return (0);

	*ptr = 0;
	*n = oldn;
	return (1);
}


/* Put an ANSI graphics rendition sequence into "b" (max length "l") which
 * changes from attributes "*c" to "d". The contents of "*c" are changed to
 * "d" on success.
 */
void vt100_ansify_attr(unsigned short *c, unsigned short d, char *b, int l)
{
	unsigned short attr_on, attr_off;	/* changing attributes */
	int fg, bg, dfg, dbg;		 /* foreground, background, desired fg&bg */
	unsigned short attr, dattr;	 /* attributes, desired attributes */
	static char buf[8];		 /* internal buffer */

	if ((!c) || (!b) || (l < 5))
		return;

	*b = 0;

	if (*c == d)
		return;			    /* nothing to change */

	attr = *c;
	fg = (attr & VT_FOREGROUND_MASK) >> VT_FOREGROUND_SHIFT;
	bg = (attr & VT_BACKGROUND_MASK) >> VT_BACKGROUND_SHIFT;
	attr ^= (attr & (VT_FOREGROUND_MASK | VT_BACKGROUND_MASK));

	dattr = d;
	dfg = (dattr & VT_FOREGROUND_MASK) >> VT_FOREGROUND_SHIFT;
	dbg = (dattr & VT_BACKGROUND_MASK) >> VT_BACKGROUND_SHIFT;
	dattr ^= (dattr & (VT_FOREGROUND_MASK | VT_BACKGROUND_MASK));

	if (dattr != attr) {		    /* need to change some attributes */
		if (dattr == 0) {	    /* turn all attributes off */
			if (mystrncat(b, "\033[m", &l))
				return;
			(*c) ^= attr;
		} else {		    /* here comes the klarty bit */

			do {
				attr = *c;
				attr ^=
				    (attr &
				     (VT_FOREGROUND_MASK |
				      VT_BACKGROUND_MASK));

				attr_on = dattr - (dattr & attr);	/* turn these ON */
				attr_off = attr - (dattr & attr);	/* turn these OFF */

				if (attr_off &
				    (VT_ATTR_BOLD | VT_ATTR_FAINT)) {
					if (mystrncat(b, "\033[22m", &l))
						return;
					(*c) ^= attr & VT_ATTR_BOLD;
					(*c) ^= attr & VT_ATTR_FAINT;
					(*c) ^= attr & VT_ATTR_STANDOUT;
					(*c) ^= attr & VT_ATTR_REVERSE;
					continue;	/* restart loop as have changed multiple things */
				}

				if (attr_off & VT_ATTR_STANDOUT) {
					if (mystrncat(b, "\033[23m", &l))
						return;
					(*c) ^= attr & VT_ATTR_STANDOUT;
				}

				if (attr_off & VT_ATTR_UNDERLINE) {
					if (mystrncat(b, "\033[24m", &l))
						return;
					(*c) ^= attr & VT_ATTR_UNDERLINE;
				}

				if (attr_off & VT_ATTR_BLINK) {
					if (mystrncat(b, "\033[25m", &l))
						return;
					(*c) ^= attr & VT_ATTR_BLINK;
				}

				if (attr_off & VT_ATTR_REVERSE) {
					if (mystrncat(b, "\033[27m", &l))
						return;
					(*c) ^= attr & VT_ATTR_REVERSE;
				}

				if (attr_on & VT_ATTR_BOLD) {
					if (mystrncat(b, "\033[1m", &l))
						return;
					(*c) |= VT_ATTR_BOLD;
				}

				if (attr_on & VT_ATTR_FAINT) {
					if (mystrncat(b, "\033[2m", &l))
						return;
					(*c) |= VT_ATTR_FAINT;
				}

				if (attr_on & VT_ATTR_STANDOUT) {
					if (mystrncat(b, "\033[3m", &l))
						return;
					(*c) |= VT_ATTR_STANDOUT;
				}

				if (attr_on & VT_ATTR_UNDERLINE) {
					if (mystrncat(b, "\033[4m", &l))
						return;
					(*c) |= VT_ATTR_UNDERLINE;
				}

				if (attr_on & VT_ATTR_BLINK) {
					if (mystrncat(b, "\033[5m", &l))
						return;
					(*c) |= VT_ATTR_BLINK;
				}

				if (attr_on & VT_ATTR_REVERSE) {
					if (mystrncat(b, "\033[7m", &l))
						return;
					(*c) |= VT_ATTR_REVERSE;
				}
			} while (attr_on | attr_off);
		}
	}

	if (dfg != fg) {		    /* need to change foreground colour */
		sprintf(buf, "\033[3%dm", dfg);
		if (mystrncat(b, buf, &l))
			return;
		attr = *c;
		attr ^= (attr & VT_FOREGROUND_MASK);
		attr |= (dfg << VT_FOREGROUND_SHIFT);
		(*c) = attr;
	}

	if (dbg != bg) {		    /* need to change background colour */
		sprintf(buf, "\033[4%dm", dbg);
		if (mystrncat(b, buf, &l))
			return;
		attr = *c;
		attr ^= (attr & VT_BACKGROUND_MASK);
		attr |= (dbg << VT_BACKGROUND_SHIFT);
		(*c) = attr;
	}

}


/* Combine the text starting at "t" and the attributes starting at "a", which
 * are both "n" items long, into one string (stored in "s", max length "l")
 * of ANSI colour text.
 *
 * Returns the number of items converted (bytes and shorts), NOT the length
 * of the resultant string. A negative return value indicates an error.
 *
 * The output string is always null terminated.
 */
int vt100_ansify(char *s, unsigned char *t, unsigned short *a, int l,
		 int n)
{
	unsigned short attr;
	char buf[64];
	int i;

	if ((!s) || (!t) || (!a))
		return (-1);
	if ((l < 1) || (n < 1))
		return (0);

	attr = VT_COLOUR_WHITE << VT_FOREGROUND_SHIFT;
	s[0] = 0;			    /* null-terminate output buffer before starting */

	for (i = 0; i < n; i++) {
		vt100_ansify_attr(&attr, a[i], buf, 64);
		if ((strlen(s) + strlen(buf) + 1) >= l)
			return (i);	    /* no room */
		strcat(s, buf);		    /* add escape sequences */
		buf[0] = t[i];
		buf[1] = 0;
		strcat(s, buf);		    /* add character from text */
	}

	return (n);
}

/* EOF */
